cocoon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Grzegorz Kossakowski <g...@tuffmail.com>
Subject [RT] The big picture of Servlet Service Framework
Date Tue, 14 Aug 2007 22:15:55 GMT
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/

Mime
View raw message