[Geoserver-devel] Rest Routing Improvements

Hey all. I was recently added to this ticket:
http://jira.codehaus.org/browse/GEOS-3436 regarding restlet route
handling. I've attached a small patch to it that addresses the basic
issue, but it brings to mind some larger concerns I have about
GeoServer's Restlet route handling. Basically, routes for restlets in
GeoServer are dynamically added based on instances of a custom
RESTMapping class in the Spring context (example from my scriptlet
extension since it's short):

   <bean id="javascriptRestletMapping"
        class="org.geoserver.rest.RESTMapping">
        <property name="routes">
            <map>
                <entry>
                    <key><value>/script</value></key>
                    <value>javascriptRestlet</value>
                </entry>
                <entry>
                    <key><value>/script/{script}</value></key>
                    <value>javascriptRestlet</value>
                </entry>
            </map>
        </property>
    </bean>

This obscures some of the extra facilities provided by Restlet; there
are some settings regarding match "scoring" etc that are made globally
in the code that consumes this extension point. The ticket addresses
one such decision; that all templates should use the default "starts
with" matching mode. Since the index Restlet is routed to the empty
string, every request matches it, if nothing else. My patch switches
this to "entire string" matching, which I think is probably appropriate
for this situation (this matching mode makes it less likely that
extensions will "steal" paths from each other.)

However, Restlet also provides "types" for the variables in templates,
which we have thus far ignored, meaning all variables in templates are
using the default type, URI_SEGMENT. However, there are further
restrictions that might be useful (digit sequences for example) and
relaxations that definitely would be handy, especially in light of
requiring full matches. We would require this to, for example, provide
a service that exposes XML documents as arbitrary-depth hierarchies.

This requires calling mutator function on the Route object created when
Router.attach is called. The function takes a map<string, variable>
from name to variable specification, so we'd need to either set up an
encoding for these in spring, or expose an extension point allowing
routings to customize the Routes they generate.

Going the all-spring route, I guess we could come up with some way of
annotating variables in routes with type info (like
"/path/{var?uri-segment}" or something. But this would be less
extensible and more of a pain to maintain, so I'm not going to explore
it in depth.

I guess more explicit XML would look like:

<bean id="javascriptRestletMapping"
    class="org.geoserver.rest.RESTMapping">
    <property name="routes">
        <list>
            <bean class="org.geoserver.rest.Route">
                <property name="path" value="/script"/>
                <property name="restlet" ref="javascriptRestlet"/>
            </bean>
            <bean class="org.geoserver.rest.Route">
                <property name="path" value="/script/{script}"/>
                <property name="restlet" ref="javascriptRestlet"/>
                <property name="variables"><map><entry>
                <key><value>script</value></key>
                <bean class="org.restlet.util.Variable">
                    <constructor-arg>9</constructor-arg>
                </bean>
                </entry></map></property>
            </bean>
        </list>
    </property>
</bean>

We might want to wrap Variable so we can do a bit of magic letting us
use the name of the type instead of its numeric value, but you get the
idea. This also exposes other features of Variable like default values.

Alternatively, we could add a hook to the RESTMapping class allowing
extensions to customize the routes after they are added (by subclassing
RESTMapping). It seems like the app-context approach is just as nice or
better, imho. We can also extend geoserver.Route in the future to
expose other restlet.Route features in the future if need be. Plus, we
can pretty easily provide backwards compatibility for existing plugins
by just doing a type-check on RESTMapping.routes (if it's a Map, we do
what we're already doing, if it's a List, we use the newer Route
objects).

Thoughts, opinions, criticisms?

--
David Winslow
OpenGeo - http://opengeo.org/

David Winslow ha scritto:

Hey all. I was recently added to this ticket:
http://jira.codehaus.org/browse/GEOS-3436 regarding restlet route
handling. I've attached a small patch to it that addresses the basic
issue, but it brings to mind some larger concerns I have about
GeoServer's Restlet route handling. Basically, routes for restlets in
GeoServer are dynamically added based on instances of a custom
RESTMapping class in the Spring context (example from my scriptlet
extension since it's short):

   <bean id="javascriptRestletMapping" class="org.geoserver.rest.RESTMapping">
        <property name="routes">
            <map>
                <entry>
                    <key><value>/script</value></key>
                    <value>javascriptRestlet</value>
                </entry>
                <entry>
                    <key><value>/script/{script}</value></key>
                    <value>javascriptRestlet</value>
                </entry>
            </map>
        </property>
    </bean>

This obscures some of the extra facilities provided by Restlet; there
are some settings regarding match "scoring" etc that are made globally
in the code that consumes this extension point. The ticket addresses
one such decision; that all templates should use the default "starts
with" matching mode. Since the index Restlet is routed to the empty
string, every request matches it, if nothing else. My patch switches
this to "entire string" matching, which I think is probably appropriate
for this situation (this matching mode makes it less likely that
extensions will "steal" paths from each other.)

Sounds good

However, Restlet also provides "types" for the variables in templates,
which we have thus far ignored, meaning all variables in templates are
using the default type, URI_SEGMENT. However, there are further
restrictions that might be useful (digit sequences for example) and
relaxations that definitely would be handy, especially in light of
requiring full matches. We would require this to, for example, provide
a service that exposes XML documents as arbitrary-depth hierarchies.

This requires calling mutator function on the Route object created when
Router.attach is called. The function takes a map<string, variable>
from name to variable specification, so we'd need to either set up an
encoding for these in spring, or expose an extension point allowing
routings to customize the Routes they generate.

Ok, I'm officially lost :slight_smile:
Where can I read more about this topic?

Going the all-spring route, I guess we could come up with some way of
annotating variables in routes with type info (like
"/path/{var?uri-segment}" or something. But this would be less
extensible and more of a pain to maintain, so I'm not going to explore
it in depth.

I guess more explicit XML would look like:

<bean id="javascriptRestletMapping" class="org.geoserver.rest.RESTMapping">
    <property name="routes">
        <list>
            <bean class="org.geoserver.rest.Route">
                <property name="path" value="/script"/>
                <property name="restlet" ref="javascriptRestlet"/>
            </bean>
            <bean class="org.geoserver.rest.Route">
                <property name="path" value="/script/{script}"/>
                <property name="restlet" ref="javascriptRestlet"/>
                <property name="variables"><map><entry>
                <key><value>script</value></key>
                <bean class="org.restlet.util.Variable">
                    <constructor-arg>9</constructor-arg>
                </bean>
                </entry></map></property>
            </bean>
        </list>
    </property>
</bean>

I'm wondering if we can get to a compromise here that does not
force us to use heavy syntax in all cases.
Something like {script} is assumed to be a plain string without "/"
inside of it if nothing else is stated.
And then have the map just to declare what is the expected type.
Or even just:

     <bean id="javascriptRestletMapping"
          class="org.geoserver.rest.RESTMapping">
          <property name="routes">
              <map>
                  <entry>
                      <key><value>/script</value></key>
                      <value>javascriptRestlet</value>
                  </entry>
                  <entry>
                      <key><value>/script/{script}</value></key>
                      <value>javascriptRestlet</value>
                  </entry>
              </map>
          </property>
          <property name="variableTypes">
              <map>
                 <entry>
                    <key>script</key>
                    <!-- silly example -->
                    <value>numeric</key>
              </map>
          </property>
      </bean>

We might want to wrap Variable so we can do a bit of magic letting us
use the name of the type instead of its numeric value, but you get the
idea. This also exposes other features of Variable like default values.

Or alternatively:

     <bean id="javascriptRestletMapping"
          class="org.geoserver.rest.RESTMapping">
          <property name="routes">
              <map>
                 ...
              </map>
          </property>
          <property name="variables">
              <list>
                 <value>
                     <bean class="org.geoserver.rest.Variable>
                         <property name="type">numeric</property>
                         <property name="default">0</property>
                     </bean>
                 </value>
              </list>
          </property>
      </bean>

A pay as you go model. I think in the majority of cases our variables
are non uri portion strings and they default to null?
For this case I would prefer not to have to specify anything (would
also make for backwards compatible approach, as we just add an
optional property to the mix)

Cheers
Andrea

--
Andrea Aime
OpenGeo - http://opengeo.org
Expert service straight from the developers.

Andrea Aime wrote:

David Winslow ha scritto:

Hey all. I was recently added to this ticket:
http://jira.codehaus.org/browse/GEOS-3436 regarding restlet route
handling. I've attached a small patch to it that addresses the basic
issue, but it brings to mind some larger concerns I have about
GeoServer's Restlet route handling. Basically, routes for restlets in
GeoServer are dynamically added based on instances of a custom
RESTMapping class in the Spring context (example from my scriptlet
extension since it's short):

   <bean id="javascriptRestletMapping" class="org.geoserver.rest.RESTMapping">
        <property name="routes">
            <map>
                <entry>
                    <key><value>/script</value></key>
                    <value>javascriptRestlet</value>
                </entry>
                <entry>
                    <key><value>/script/{script}</value></key>
                    <value>javascriptRestlet</value>
                </entry>
            </map>
        </property>
    </bean>

This obscures some of the extra facilities provided by Restlet; there
are some settings regarding match "scoring" etc that are made globally
in the code that consumes this extension point. The ticket addresses
one such decision; that all templates should use the default "starts
with" matching mode. Since the index Restlet is routed to the empty
string, every request matches it, if nothing else. My patch switches
this to "entire string" matching, which I think is probably appropriate
for this situation (this matching mode makes it less likely that
extensions will "steal" paths from each other.)

Sounds good

However, Restlet also provides "types" for the variables in templates,
which we have thus far ignored, meaning all variables in templates are
using the default type, URI_SEGMENT. However, there are further
restrictions that might be useful (digit sequences for example) and
relaxations that definitely would be handy, especially in light of
requiring full matches. We would require this to, for example, provide
a service that exposes XML documents as arbitrary-depth hierarchies.

This requires calling mutator function on the Route object created when
Router.attach is called. The function takes a map<string, variable>
from name to variable specification, so we'd need to either set up an
encoding for these in spring, or expose an extension point allowing
routings to customize the Routes they generate.

Ok, I'm officially lost :slight_smile:
Where can I read more about this topic?

Going the all-spring route, I guess we could come up with some way of
annotating variables in routes with type info (like
"/path/{var?uri-segment}" or something. But this would be less
extensible and more of a pain to maintain, so I'm not going to explore
it in depth.

I guess more explicit XML would look like:

<bean id="javascriptRestletMapping" class="org.geoserver.rest.RESTMapping">
    <property name="routes">
        <list>
            <bean class="org.geoserver.rest.Route">
                <property name="path" value="/script"/>
                <property name="restlet" ref="javascriptRestlet"/>
            </bean>
            <bean class="org.geoserver.rest.Route">
                <property name="path" value="/script/{script}"/>
                <property name="restlet" ref="javascriptRestlet"/>
                <property name="variables"><map><entry>
                <key><value>script</value></key>
                <bean class="org.restlet.util.Variable">
                    <constructor-arg>9</constructor-arg>
                </bean>
                </entry></map></property>
            </bean>
        </list>
    </property>
</bean>

I'm wondering if we can get to a compromise here that does not
force us to use heavy syntax in all cases.
Something like {script} is assumed to be a plain string without "/"
inside of it if nothing else is stated.
And then have the map just to declare what is the expected type.
Or even just:

     <bean id="javascriptRestletMapping"
          class="org.geoserver.rest.RESTMapping">
          <property name="routes">
              <map>
                  <entry>
                      <key><value>/script</value></key>
                      <value>javascriptRestlet</value>
                  </entry>
                  <entry>
                      <key><value>/script/{script}</value></key>
                      <value>javascriptRestlet</value>
                  </entry>
              </map>
          </property>
          <property name="variableTypes">
              <map>
                 <entry>
                    <key>script</key>
                    <!-- silly example -->
                    <value>numeric</key>
              </map>
          </property>
      </bean>

We might want to wrap Variable so we can do a bit of magic letting us
use the name of the type instead of its numeric value, but you get the
idea. This also exposes other features of Variable like default values.

Or alternatively:

     <bean id="javascriptRestletMapping"
          class="org.geoserver.rest.RESTMapping">
          <property name="routes">
              <map>
                 ...
              </map>
          </property>
          <property name="variables">
              <list>
                 <value>
                     <bean class="org.geoserver.rest.Variable>
                         <property name="type">numeric</property>
                         <property name="default">0</property>
                     </bean>
                 </value>
              </list>
          </property>
      </bean>

A pay as you go model. I think in the majority of cases our variables
are non uri portion strings and they default to null?
For this case I would prefer not to have to specify anything (would
also make for backwards compatible approach, as we just add an
optional property to the mix)

This is more along the lines of what I had in mind as well when I heard the initial pitch yesterday. But I will freely admit I have not looked at all into the problem.

Cheers
Andrea

--
Justin Deoliveira
OpenGeo - http://opengeo.org
Enterprise support for open source geospatial.

On Fri, 2009-09-11 at 12:20 +0200, Andrea Aime wrote:

David Winslow ha scritto:
> However, Restlet also provides "types" for the variables in templates,
> which we have thus far ignored, meaning all variables in templates are
> using the default type, URI_SEGMENT. However, there are further
> restrictions that might be useful (digit sequences for example) and
> relaxations that definitely would be handy, especially in light of
> requiring full matches. We would require this to, for example, provide
> a service that exposes XML documents as arbitrary-depth hierarchies.
>
> This requires calling mutator function on the Route object created when
> Router.attach is called. The function takes a map<string, variable>
> from name to variable specification, so we'd need to either set up an
> encoding for these in spring, or expose an extension point allowing
> routings to customize the Routes they generate.

Ok, I'm officially lost :slight_smile:
Where can I read more about this topic?

I linked to the Javadocs for org.restlet.util.Variable on the ticket;
the Route and Template docs are also relevant. I'm not aware of any
more in-depth documentation on them.

http://www.restlet.org/documentation/1.0/api/org/restlet/util/Variable.html
http://www.restlet.org/documentation/1.0/api/org/restlet/util/Template.html
http://www.restlet.org/documentation/1.0/api/org/restlet/Route.html

Basically, for each path/restlet pair in the code, we call
Router.attach(path, restlet);
which returns a Route for further manipulation, but we ignore it now.
For the variable-related changes, we'd need to do:
Route r = router.attach(path, restlet);
r.getTemplate().setMatchingMode(Template.MODE_EQUALS);
r.getTemplate().setVariables(somemapofnamestovariables);

I'm wondering if we can get to a compromise here that does not
force us to use heavy syntax in all cases.
Something like {script} is assumed to be a plain string without "/"
inside of it if nothing else is stated.
And then have the map just to declare what is the expected type.

A pay as you go model. I think in the majority of cases our variables
are non uri portion strings and they default to null?
For this case I would prefer not to have to specify anything (would
also make for backwards compatible approach, as we just add an
optional property to the mix)

Cheers
Andrea

I agree, it doesn't make sense to require a variable descriptor for
variables that are fine with the default configuration. It is probably
also fine to have a shared variable map for all routes, but then it is
essential that developers use variable names consistently in a set of
routes. I am also unsure whether restlet chokes on variable descriptors
for variables that aren't in the route (stranger things have happened).
I will investigate later today.

--
David Winslow
OpenGeo - http://opengeo.org/

On Fri, 2009-09-11 at 11:57 -0400, David Winslow wrote:

On Fri, 2009-09-11 at 12:20 +0200, Andrea Aime wrote:
> David Winslow ha scritto:
> > However, Restlet also provides "types" for the variables in templates,
> > which we have thus far ignored, meaning all variables in templates are
> > using the default type, URI_SEGMENT. However, there are further
> > restrictions that might be useful (digit sequences for example) and
> > relaxations that definitely would be handy, especially in light of
> > requiring full matches. We would require this to, for example, provide
> > a service that exposes XML documents as arbitrary-depth hierarchies.
> >
> > This requires calling mutator function on the Route object created when
> > Router.attach is called. The function takes a map<string, variable>
> > from name to variable specification, so we'd need to either set up an
> > encoding for these in spring, or expose an extension point allowing
> > routings to customize the Routes they generate.
>
> Ok, I'm officially lost :slight_smile:
> Where can I read more about this topic?

I linked to the Javadocs for org.restlet.util.Variable on the ticket;
the Route and Template docs are also relevant. I'm not aware of any
more in-depth documentation on them.

http://www.restlet.org/documentation/1.0/api/org/restlet/util/Variable.html
http://www.restlet.org/documentation/1.0/api/org/restlet/util/Template.html
http://www.restlet.org/documentation/1.0/api/org/restlet/Route.html

Basically, for each path/restlet pair in the code, we call
Router.attach(path, restlet);
which returns a Route for further manipulation, but we ignore it now.
For the variable-related changes, we'd need to do:
Route r = router.attach(path, restlet);
r.getTemplate().setMatchingMode(Template.MODE_EQUALS);
r.getTemplate().setVariables(somemapofnamestovariables);

> I'm wondering if we can get to a compromise here that does not
> force us to use heavy syntax in all cases.
> Something like {script} is assumed to be a plain string without "/"
> inside of it if nothing else is stated.
> And then have the map just to declare what is the expected type.

> A pay as you go model. I think in the majority of cases our variables
> are non uri portion strings and they default to null?
> For this case I would prefer not to have to specify anything (would
> also make for backwards compatible approach, as we just add an
> optional property to the mix)
>
> Cheers
> Andrea

I agree, it doesn't make sense to require a variable descriptor for
variables that are fine with the default configuration. It is probably
also fine to have a shared variable map for all routes, but then it is
essential that developers use variable names consistently in a set of
routes. I am also unsure whether restlet chokes on variable descriptors
for variables that aren't in the route (stranger things have happened).
I will investigate later today.

--
David Winslow
OpenGeo - http://opengeo.org/

I just ran some tests (I was slowed down a bit by some problems with the
URL mangler but aaime fixed them before I even figured out what was
wrong :slight_smile: and it looks like having superfluous variable descriptors
doesn't cause any problems. So, I am +1 on having a single variable
map shared for all routes in a RESTMapping instance. I'd be happy to
put together a patch, but I think I'll hold off on doing so until #3436
is closed.

--
David Winslow
OpenGeo - http://opengeo.org/