Hi Cocooners,

I promised to give an overview on our Servlet Service Framework, its status and 
relation to the
Resource-Oriented Architecture (ROA) and RESTful design principles. My 
knowledge about REST comes
mainly from RESTful Web Services[1] by Leaonard Richardson and Sam Ruby. I'll refer and quote it extensively using RSW abbrevation.

Sorry for taking so long to write this mail but I had serious problems with my ISP so I was often offline. It's my first [RT] mail so I kindly ask you to be gentle when you appraise.

                                              - o -


  Servlet Service Fw keeps Virtual Sitemap/Pipeline Components promise
  --------------------------------------------------------------------

Virtual Sitemap (or better, Pipeline) Components has been talked about for very 
long time. At spring
of 2005 Daniel said[2] that it was very near to usable implementation in trunk. 
I don't remember
further course of events but current status of that implementation is that it 
is laying in
whiteboard for rather educational purposes. There is some code that could be 
probably reused for
other purposes.

Development of that implementation has been suspended because other 
implementation (framework)
provides very similar functionality to the VPCs in more standard-compliant and 
Cocoon-independent
way. The framework I'm talking about is SSF (Servlet Service Framework), of 
course. It keeps promise
of VPCs by somehow by "accident".

Main purpose of SSF existence is to provide convenient for servlets 
collaboration, reuse and
connectivity. You can easily mount any class implementing 
javax.servlet.http.HttpServlet interface
at any path you like by using this simple Spring configuration:

    <bean id="demo.servlet" class="demo.DemoServlet">
      <servlet:context mount-path="/demo"/>
    </bean>

As has been said earlier, servlet can be connected to another servlet:

    <bean id="demo.servlet" class="demo.DemoServlet">
      <servlet:context mount-path="/demo">
        <servlet:connections>
          <!-- "demo2" is local name identifying connection to the 
demo.OtherDemoServlet bean -->
          <entry key="demo2" value-ref="demo.OtherDemoServlet"/>
        </servlet:connections>
      </servlet:context>
    </bean>

This way one servlet can reference resources from another servlet by using 
simple URIs:

  servlet:demo2:/file

This URI identifies 'file' resource of demo.OtherDemoServlet. It's possible to 
resolve such URIs
because SSF provides custom implementation of Excalibur's Source interface. 
After resolving the URI
you can fetch actual resource of course; it is performed using standard HTTP 
(and servlet) method:
HTTP GET. It's important that SSF does not introduce new interfaces or 
paradigms, it makes use
purely of HTTP and Servlet specification for resource maniupulation. I'll come 
back to this
characteristic of SSF.

Now you know what's main purpose for SSF but you may start to wonder how it 
relates to VPCs. The
whole idea concentrates around question: if servlet source can give access to 
the data produced by
another servlet why cannot it pass that another servlet incoming data that it 
can compute resource
of? In other words, why can't we pass some input for servlet when calling it? 
It was Daniel who
wondered[3] about this, first time. As result of the discussion proposal for 
Postable
source has been made[4] by Daniel.

The idea was quite simple: if we use HTTP GET to fetch data, what about using 
HTTP POST to sent some
input? If you take into account that, in Cocoon 2.2, sitemap of every block is 
a servlet and let
special pipeline components to POST the data you get what Daniel proposed and 
what is, in a fact,
VPCs implementation. Current implementation has few limitations:
  * SAX events are serialized and parsed again when making POST request to the 
servlet service
  * caching is not supported at any mean
  * environmental data (request, session, flowscript context aka bizData) of 
caller is not
available in the called service

If you want to have more detailed look on postable source implementation take a 
look at
COCOON-2046[5] an COCOON-2050[6]. To see postable source in action, take a look 
at
servlet-service-sample module[7].

What needs emphasis it the use of POST method and understanding of "service" 
word. When Postable
Source is used in servlet service pipeline components, the HTTP POST method is 
simply used to feed
called pipeline (or servlet, in general) with data it is going to operate and 
is going to produce a
result from.
Nothing less, nothing more.
In this case called servlet shares "service" that should be understood as "an 
*resource* identified
by URI that takes input data and produces output data usually _whithout_ side 
effects". That means,
this kind of service should never support methods like PUT or DELETE. You 
should consider such
service as pipeline fragment or VPC that uses POST method as low-level detail 
of carrying data.

What's more, such service is not even close to ROA because it is not stateless. 
RSW book makes
statelessness one of requirements for ROA and describes it that way:

    /Statelessness/ means that every HTTP request happens in complete 
isolation. When client makes
    an HTTP request, it includes all information necessary for the server to 
fulfil that request.
    The server never relies on information from previous requests. If that 
information was
    important, the client would have sent it again in this request.

Service calls that makes use of Cocoon's environment forwarding (not 
implemented at the time) are
not stateless because request does not contain all needed data. Moreover, 
environment forwarding is
usually required to make use of sessions or continuations that are in contrast 
to the statelessness.

The conclusion is that kind of services are rather tied acting as VPCs and 
legacy servlet
integration have nothing to do with ROA so also with REST design rules.

                                              - o -


  Approaching the ROA and REST
  ----------------------------

What if we could relinquish environment forwarding? We could get stateless 
service of course.
Quoting RSW again:

    -- Creating subordinate resource --
    In a RESTful design, POST is commonly used to create subordinate resources: 
resources that exist
    in relation to some other "parent" resource. (..) To create a weblog entry 
or a database record,
    you POST to the parent: the weblog or database table.
    (..)
    Why can't you just use PUT to create subordinate resources? Well, sometimes 
you can. (..) The
    difference between PUT and POST is this: the client uses PUT when it's in 
chage of deciding
    which URI the new resource should have. The client uses POST when the 
server is in charge of
    deciding which URI the new resource should have.

And another sub chapter:

    -- Overloaded POST: The not-so-uniform interface --
    (..)
    The one use of POST I haven't explained is the one you're probably most 
familiar with, because
    it's the one that drives almost all web applications: providing a block of 
data, such as the
    result of submitting a form, to a data-handling process.

    What's a "data-handling process"? That sounds pretty vague. And, indeed, 
just about anything can
    be a data-handling process. Using POST this way turns a resource into a 
tiny message processor
    that acts like an XML-RPC server. The resource accepts POST requests, 
examines the request, and
    decides to do... something. Then it decides to serve to the client... some 
data...

    I call this use of POST /overloaded POST/, by analogy to operator 
overloading in a programming
    language. It's overloaded because a single HTTP method is being used to 
signify any number of
    non-HTTP methods. (..)

    As a REST partisan I don't like this very much, but occasionally it's 
unavoidable.


All I want to say here is that our services in Cocoon, when stateless, are 
somewhere in between true
REST and use of overloaded POST. It's not true REST for obvious reason - our 
services do not create
subordinate resources at any means. And it does not use truly overloaded POST 
because our service is
not really general XML-RPC server, it acts as such somehow. Surely our services 
examine requests do
something (execute pipeline) and serve to the client the result of pipeline's 
execution.

Even authors of RSW book admit that overloaded POST is good choice from time to time. My own opinion is that it's perfectly valid to use overloaded POST to implemented our servlet services. What's more, if you do so only in this particular case (servlet service implementation) we still have a chance to have Resource-Oriented Architecture.

                                              - o -


  Attaining ROA nirvana with Cocoon
  -------------------------------------

This paragraph is going to describe my own view on achieving ROA and as consequence RESTful-compliant design. I'll try to describe relation of various components we already have to achieving ROA.

  -- Sitemap --
It's old but still powerful component in Cocoon. As long as we deal with URIs sitemap's matching mechanisms will be relevant. Take a look at quotation from RWS book, "When In Doubt, Make It a Resource" sub-chapter:
    (..)
    The larger point of this section is that when I say "anything can be a 
resource" I do mean
    /anything/. If there's a concept that's causing you design troubles, you 
can usually fit it into
    the ROA by exposing it as a new kind of resource. (..)

This indicates that as consequence of ROA one is going to have plenty of resources. Each resource has an URI that must be handled somehow. Here power of sitemap's matchers can really shine. The RWS book makes a lot of examples in Ruby and showed in Example 7-3 code of routes.rb file. I'll give you some snippet so you can get an idea of it's purpose:
  ActionController::Routing::Routes.draw do |map|
    base = '/v1'

    ## The first controller I define is the UsersController. The call to
    ## map.resources sets it up so that all HTTP request to /v1/users
    ## or /v1/users{username} are routed to the UsersController class.

    # /v1/users => UsersController
    map.resources :users, :path_prefix => base

    ## Now I'm going to define a number of controllers beneath the
    ## UsersController. They will respond to requests for URIs that start out
    ## with /v1/users/{username}, and then have some extra stuff.
    user_base = base + '/users/:username'

    # /v1/users/{username}/bookmarks => BookmarksController
    map.resources :bookmarks, :path_prefix => user_base

    [..]
  end

You probably guessed it, routes.rb file acts as simple dispatcher that dispatches requests to the controller. Quite the same could be achieved with our sitemap engine:

  <map:sitemap>
    <map:pipeline>

      <map:match pattern="/v1/users/*/bookmarks/*">
        <map:call function="bookmarksController_handle{$cocoon/request/method}">
          <map:parameter name="username" value="{1}"/>
          <map:parameter name="bookmark" value="{2}"/>
        </map:call>
      </map:match>

      <map:match pattern="/v1/users/*">
        <map:call function="usersController_handle{$cocoon/request/method}">
          <map:parameter name="username" value="{1}"/>
        </map:call>
      </map:match>

    </map:pipeline>
  </map:sitemap>

This code could be done much compact by coming up with conventions but it's topic for another discussion, really.

  -- Servlet Service Framework --
It's new, hot part of Cocoon and really ROA-enabled one. I think one of the most important features of SSF from both ROA and implementation POVS is ability to mount new servlets (blocks) at certain URI and ability to extend[8] URI space of one servlet by another one.

SSF enable you to easily map structure of your app (blocks) to URI design and handle any number of resources very easily. If one block becomes overloaded by number of resource it has to handle you can always break it into two without any pain at all.

When extending one block by another, you extend URI space so there is no magic behind the scene here, either. You can go with such advanced functionality like extension of existing blocks and stay 100% ROA-compliant. Actually, current architecture of SSF enforces on you to some extent being ROA-compliant.

I think that servlet: source concentrates all the power of SSF design. So 
what's servlet: source? It is:
* scheme that it lets you to address resources from other servlets (it's actually meta-address, more below)
  * normal Excalibur source that lets you to ask other servlets for resource's 
data
  * very convenient links factory that is aware of servlet inheritance

First and third points are tightly coupled. Servlet URIs are, in a fact, meta-URIs or better relative URIs. If you write such URI:
servlet:mapsBlock:/map1/town2

It only makes sense only if you now to which servlet configuration it belongs to because it is going to be resolved against servlet's connections map and servlet inheritance will have to be taken into consideration. Servlet URIs are relative to particular configuration.

RWS book mentions connectedness as one of the most important indicators of ROA design. If we want to make a web service we need to fulfil "web" part requirements. Web is about hyper links connecting resources and we have to create a lot of links. Here servlet: source power shines because you use exactly the same scheme for accessing the resource on the server side (in sitemaps) and you put them into templates of the content served to the client. And you don't have to bother when another servlet extends your servlet, servlet: source *will* respect that for free.

  -- Flowscript --
When approaching ROA design we need to throw out continuations of course. So what's the use of flowscript, then?

It's my very own opinion but I think that our controllers should be sitemap(as dispatcher)+script(containing actual controller's code). I think that most of us would agree that dynamic languages have some features really useful for fast prototyping and glueing code.

I take role of flowscript and pipelines (below) really briefly because it's quite far from big picture of Servlet Service Framework. :-)

  -- Pipelines --
Our pipelines already proved to be very powerful in publishing framework. If we think about Cocoon as RESTful framework we can be sure that our pipelines will still be doing great job when we want to serve resource's representation that would be usually custom XML or XHTML.

                                              - o -

Ok, that's all for now. Thanks for reading.

[1] ISBN: 0-596-52926-0 http://www.oreilly.com/catalog/9780596529260/
[2] http://article.gmane.org/gmane.text.xml.cocoon.devel/49148
[3] http://article.gmane.org/gmane.text.xml.cocoon.devel/67480
[4] http://article.gmane.org/gmane.text.xml.cocoon.devel/67487
[5] https://issues.apache.org/jira/browse/COCOON-2046
[6] https://issues.apache.org/jira/browse/COCOON-2050
[7]
http://svn.apache.org/repos/asf/cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-sample/
[8] https://issues.apache.org/jira/browse/COCOON-2038

--
Grzegorz Kossakowski
http://reflectingonthevicissitudes.wordpress.com/

Reply via email to