cocoon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Stefano Mazzocchi <stef...@apache.org>
Subject [RT] FOM
Date Tue, 27 May 2003 06:44:45 GMT
Please, consider that even if I'm writing this, the ideas presented on
this RT were distilled over several evenings spent with Ricardo on the
whiteboard. As so, we are presenting them to you, hoping to sparkle
further discussion.

                                  - o -

The FOM is the Flow Object Model. In my vision, it's the server-side
equivalent of the DOM.

Pier and I already stated a while back that our current implementation
of the FOM is weak and its design poor. I had those feelings but I
couldn't really come up with a real design reason for that, but now I
have it: the current object model tries to wrap around everything
without considering if it's good or bad to have access to the
environment hooks.

I believe that the number of methods/properties/objects should be
reduced to the minimum necessary. Why so? several reasons:

 1) incremental evolution: adding a method because something we thought
it wastn't needed becomes needed doesn't create back-incompatibilities,
removing a method because we find out how much it can be abused, it does.

[NOTE: the sitemap has been designed with 'less is more' and every
addition has been challenged deeply. Today, everybody sees the sitemap
as an elegant and coherent contract]

 2) design for safety: the flow will be a center of abuse because people
will find it easier to write longer flows than to restructure their
business logic into components. We must make all possible efforts to
reduce this from happening.

                             - o -

First of all, as a general rule and in order to keep a sort of object
orientation, no global methods or properties are kept. The only global
contract is the "cocoon" object which is used as an entry point between
the flow and cocoon. [this is equivalent of the "window" object for DOM]

---------------------------------------------------------------------------
The Cocoon Object
---------------------------------------------------------------------------

The Cocoon object is the only possible way the flow can communicate with
Cocoon. In a sense, it's a gateway between the two realms. This protects
the flow from abusing the Cocoon internals (for example, there is no way
the flow can compose a sitemap pipeline since the cocoon object doesn't
give it that power)

--------- methods ------------

 void sendPage(uri,map) -> returns control to the sitemap, invoking the
pipeline that will match the given URI and passing the given map as model.

 void sendPageAndWait(uri,map) -> same as above, but creates one (or
more) continuation objects and makes their IDs available as part of the
model passed.

 void processPipelineTo(uri,map,outputStream) -> invoques the pipeline
that will match the given URI, passing the map as the model but
connecting the output of the pipeline to the given output stream.

 void redirectTo(uri) -> triggers a client-side redirect to the given URI

 void addEventListener(eventName,eventHandler) -> adds an event listener
to the given event name (for example, session expiration)

 void removeEventListener(eventName,eventHandler) -> removes the given
handler from listening the tiven event name.

 Object getComponent(id) -> obtains the component indicated by the given ID

 void callAction(name,map) -> invoques the action indicated by the given
name and pass the given map as model

NOTE: I personally believe that the getComponent() method removes all
needs for the callAction() method. I foresee a future where the
callAction() method will be deprecated. I would personally go the extra
mile and avoid having it there altogether, but since there is no easy
way to plugin new avalon components at runtime (at least, not as easier
as plugging in different actions into the sitemap), I'm in favor of
leaving it for now, until 'real blocks' will make it unnecessary.

 Session getSession()

NOTE: both Ricardo and I believe that the flow should always be
associated with a Session. Thus the use of the semantics "getSession"
instead of "createSession". We are, in fact, against the concept of
having the flow behave differently we the session has been created or
it's has not been. This implicit behavior is potentially very dangerous
from a user perspective and should be avoided.

Moreover, it has been pointed out how continuations can be see as a way
to "extend" sessions rather than replacing them. This would allow us to
reuse the session-handling machinery already in place for things like
load-balancing and fault-tollerance.

NOTE: methods that were left out where

 - input/output module support
 - script load support

the reason for the first goes together with callAction(). Input/output
modules were designed to overcome limitations in the scriptability of
the sitemap.

The reason for removing load() is because we want to avoid people from
loading scripting dynamically. This goes in parallel with the
anti-pattern of dynamic pipeline construction.

WARNING: removing load() does *NOT* imply that you have to force all
your flow in one big file. The way to fragment your flow into different
files is to use several <map:script> elements in the <map:flow> section
of the sitemap.

-------- properties ---------

 cocoon.request -> the request object
 cocoon.response -> the response object
 cocoon.log -> the log object

NOTE: the absence of the context object is intentional! we couldn't come
up with a reasonable need for such an object at the flow level. So, for
the principle of 'less is more', we don't consider it. Be aware that if
you want to propose its addition, you have to come up with a reason for it.


---------------------------------------------------------------------------
The Session Object
---------------------------------------------------------------------------

The session object is simply a wrapper around the Session object
provided by the Cocoon environment. No methods were left out since they
don't provide problems to the flow environment or suggest abuse.

---- methods -----

attributes
 getAttribute(name)
 setAttribute(name,value)
 removeAttribute(name)
 getAttributeNames()

control
 invalidate()
 isNew()
 getId()

time
 getCreationTime()
 getLastAccessedTime()
 setMaxInactiveInterval(interval)
 getMaxInactiveInterval()

---- properties -----

 [name] -> maps to the attribute indicates with the name

For example

 session.blah

is equivalent to

 session.getAttribute("blah")


---------------------------------------------------------------------------
The Response Object
---------------------------------------------------------------------------

The response object contains hooks only to the cookies and to the
response headers. Everything else will be controlled by the rest of the
cocoon pipeline machinery (like output encoding, for example, which
should *NOT* be determined by the flow)

------- methods -------

cookies
  Cookie createCookie(name,value)
  void addCookie(Cookie cookie);

headers
  containsHeader(name)
  setHeader(name,value)
  addHeader(name,value)

------- properties -----

NONE


---------------------------------------------------------------------------
The Cookie Object
---------------------------------------------------------------------------

This object is a wrapper around the Cookie object provided by the
Environment.

--------- methods -------

 getName()
 getVersion()
 setVersion(version)

values
 setValue(value)
 getValue()

comment
 setComment(purpose)
 getComment

domain
 setDomain(domain)
 getDomain()

age
 setMaxAge(age)
 getMaxAge()

path
 setPath(path)
 getPath()

secure
 setSecure(secure)
 getSecure()

-------- properties ------

 NONE

---------------------------------------------------------------------------
The Request Object
---------------------------------------------------------------------------

The Request object is the one who has been pruned the most from the
Cocoon Environment. The sections that were pruned were:

 - URI handling
 - Session handling

Session handling has been removed in favor of the cocoon object and URI
handling has been removed because unnecessary. I'm aware this is a big
statement, please see below of why this is so.

----- methods ------

attributes
 get(name)
 getAttribute(name)
 getAttributeNames()
 setAttribute(name,value)
 removeAttribute(name)

encoding
 getCharacterEncoding()
 setCharacterEncoding(encoding)

payload
 getContentLength()
 getContentType()

parameters
 getParameter(name)
 getParameterValues(name)
 getParameterNames()

transport
 getAuthType()
 getProtocol()
 getScheme()
 getMethod()
 getServerName()
 getServerPort()
 getRemoteAddr()
 getRemoteHost()
 isSecure()

locale
 getLocale()
 getLocales()

cookies
 getCookies()

headers
 getHeader()
 getHeaders()
 getHeadersNames()

authentication
 getRemoteUser()
 getUserPrincipal()
 isUserInRole(role)

-------- properties ---------

 [name] -> maps to the parameter indicates with the name

For example

 request.blah

is equivalent to

 request.getParameter("blah")

---------------------------------------------------------------------------
The Log Object
---------------------------------------------------------------------------

This is a convenience method to access the cocoon logger. It could be
the same of doing

 cocoon.getComponent(...)

but since I removed the global print() method, I wanted to have an easy
way to log (mostly for debugging purposes).

------- methods --------

 debug(message)
 info(message)
 warn(message)
 error(message)

------ properties ------

 NONE

                                --- o ---

That's it.

This is, IMO, what the FOM requires. Nothing less, nothing more. But let
me explain why I consider flow-driven URI handling harmful.

Today, once you mount your web application, you don't know where it
resides. In order to keep your webapp relocatable on your URI space,
either you keep everything relative (including the many horrible
../../../ which is easy to get wrong) or you re-invert the control by
finding out yourself where you have been mounted and tweak URI accordingly.

I strongly dislike both approaches because they are error prone.

IMO, it should be cocoon to transparently adapt the URI right before
serializing the output to the client.

How?

Using a link-translating protocol.

Example:

 <html>
  <head>
   ...
   <link src="link:/styles/main.css" ...>

is then transformed accordingly (and transparently) by the sitemap right
before the serializer.

Ok, I think I wrote enough for now.

Fire at will.

-- 
Stefano.



Mime
View raw message