UCW Source

The RERL Protocol 

A UCW server sits in a tight loop waiting for incoming HTTP requests, elaborating them and then sending out the HTTP responses. This loop is called the "RERL" (Request-Eval-Response Loop) and the objects, methods are variables involved is the RERL are described here.

Components 

(defclass component ()
  ()
  (:documentation "The generic super class of all components."))
(defgeneric (setf component.calling-component) (caller component))
(defgeneric component.continuation (component)
  (:documentation "Accessor for COMPONENT's continuation.

The continuation is implemented as a 1 arg lambda which is called
with the value COMPONENT answers. and continues whatever was
stopped when control was transfered to the component."))
(defgeneric (setf component.continuation) (k component))
(defgeneric call-component (caller callee)
  (:documentation "Transfer control from the component CALLER, to
  the component CALLEE.

CALLEE should replace CALLER in the user interface (by setting
itself in CALLER's place). CALLEE can return control to CALLER by
calling answer-component.

call-component can be called by user code even though it will
generally be called through the CALL macro."))
(defgeneric answer-component  (callee value)
  (:documentation "Return control to CALLEE's CALLER, continuing
  with the value VALUE.

answer-component can be called by user code though it will
generally be called through the ANSWER macro."))
(defvar *current-component* :unbound
  "When rendering this is bound to the current component (the
  second argument passed to render-on).")
(defgeneric parent (component)
  (:documentation "Returns the parent of COMPONENT.

Only window components and detached components may return NIL
from this function."))

The rendering protocol 

(defgeneric render-on (response component)
  (:documentation "The generic entry point for rendering
  components to the user.")
  (:method-combination wrapping-standard))
(defgeneric compute-url (component &key action-id)
  (:documentation "Return a URL with the proper session, frame
  and action-id parameters."))
(defgeneric update-url (component url)
  (:documentation "Prepare URL for rendering as action urls."))

Servers 

(defvar *default-server* nil
  "The server object to use when none is explicitly specified.")
(defclass server ()
  ()
  (:documentation "A single UCW server.

Within the RERL server objects provide the following
functionality:

* Take request and response objects as provided by the backend
  via the handle-request method and run one iteration of the
  RERL.

* Call the generic function associated-application to determine
  the application object which should handle the request.

* Create a request-context object (via a call to
  make-request-context on the application) and initialize it.

* Call the application's service method passing it the
  request-context.

* Shutdown the request and response when the application's
  service method returns.

The server object should also attempt to deal with all conditions
signalled during the service'ing of the request.

Servers are responsible for managing multiple applications within
the same UCW instance and dealing with, possibly, multiple
backends."))
(defgeneric server.applications (server)
  (:documentation "Return a list of all the application objects
  SERVER manages."))
(defgeneric startup-server (server)
  (:documentation "Make SERVER start responding to requests."))
(defgeneric shutdown-server (server)
  (:documentation "Make SERVER stop responding to requests and
  close/release and resources."))
(defgeneric associated-application (server request)
  (:documentation "Return the application object which will
  handle REQUEST."))
(defgeneric handle-request (server request response)
  (:documentation "Perform one iteration of the RERL on REQUEST and RESPONSE.

Methods defined on this generic function must be built from the
methods associated-application and service and should, as much as
possible, handle all conditions signalled by calls to service."))

Applications 

(defvar *default-application* nil
  "The application object to use when none is explicitly specified.")
(defmacro in-application (application)
  `(setf *default-application* ,application))
(defclass application ()
  ()
  (:documentation "A UCW application.

Application objects are responsible for:

- Managing a collection of sessions and finding the right session for a given request.

- Managing session life time (session creation and expiration).

- Creating request contexts.

- Managing entry-points and associating them to request objects.

- Creating http query paths which subsequently (when requested)
  call action lambdas.

Since application objects are used to create sessions (via
make-new-session) they are also important when customizing the
functionality of the RERL."))
(defgeneric application.url-prefix (application)
  (:documentation "Returns the url prefix (a string) for APPLICATION.

The URL prefix is that string which, when used as the prefix of
an incoming http query path, identifies an application."))
(defgeneric make-request-context (application request response)
  (:documentation "Create a new request-context form REQUEST and RESPONSE.

The returned context will have its application, request and
response slots set to APPLICATION, REQUEST and RESPONSE.

This method need not neccessarily return a new object each time
but may reuse previouly created objects in which case a call to
clear-context must be made before returning the object."))
(defgeneric find-session (application context)
  (:documentation "Return the session object in CONTEXT or NIL if
  there's no session."))
(defgeneric make-new-session (application)
  (:documentation "Return a new session object."))
(defgeneric find-entry-point (application context)
  (:documentation "Returns the entry-point object for CONTEXT's request.

If no such entry-point exists return NIL."))
(defgeneric remove-expired-sessions (application)
  (:documentation "Remove all the expired sessions in APPLICATION.

Implementations of this method must use the generic function
EXPIREDP to determine if a session is expired or not, and must
call DELETE-SESSION on the session objects."))
(defgeneric delete-session (application session &optional expire)
  (:documentation "Remove the session SESSION from APPLICATION.

If EXPIRE is T then EXPIRE-SESSION MUST be called on the
session."))
(defgeneric startup-application (application)
  (:documentation "Start the application's request-eval-response loop."))
(defgeneric shutdown-application (application)
  (:documentation "Terminate the application's life cycle.

Release any and all resources held by APPLICATION. The value
returned by the generic function is unspecified."))

Request Contexts 

(defclass request-context ()
  ()
  (:documentation "The generic super class of all request contexts.

A request-context object contains all the information associated
with one particular request/response loop. The request context is
usually accessed via the special variable *context* and contains
the currently relevant application, session, session-frame,
request and response object."))
(defgeneric context.request (context)
  (:documentation "Accessor for the request object in CONTEXT."))
(defgeneric (setf context.request) (request context))
(defgeneric context.response (context)
  (:documentation "Accessor for the response object in CONTEXT."))
(defgeneric (setf context.response) (response context))
(defgeneric context.application (context)
  (:documentation "Accessor for the application object in CONTEXT."))
(defgeneric (setf context.application) (application context))
(defgeneric context.session (context)
  (:documentation "Accessor for the session object in CONTEXT."))
(defgeneric (setf context.session) (session context))
(defgeneric context.current-frame (context)
  (:documentation "Return the \"current\" (most recent)
  session-frame in CONTEXT's session.")
  (:method ((context request-context))
    "Simply call session.current-frame on context's session object."
    (session.current-frame (context.session context))))
(defgeneric context.window-component (context)
  (:documentation "Return the \"current\" (most recenct) window
  component in CONTEXT's session."))
(defgeneric (setf context.window-component) (component context))
(defgeneric call-callbacks (context)
  (:documentation "Call all the request callbacks in CONTEXT's request.

Methods defined on this generic function must be built up from
calls to map-parameters (part of the backend protocol) and
call-callback."))
(defgeneric find-session-id (context)
  (:documentation "Returns the client supplied session-id in CONTEXT.

Methods should inspect the context's request object and return a
string specifying the session-id. No guarantee on the
validity (well-formedness or existence) of the returned
session-id is made.

If no session-id is supplied NIL must be returned."))
(defgeneric find-frame-id (context)
  (:documentation "Same as find-session-id but looks for the frame id."))
(defgeneric find-action-id (context)
  (:documentation "Same as find-action-id but looks for the action id."))
(defgeneric clear-context (context)
  (:documentation "Prepare REQUEST-CONTEXT for re-use.

This method must destructivly modify CONTEXT so that it becomes
indistinguishable from a freshly created object as returned by
make-request-context."))

The generic function SERVICE  

Service method and various constants and variables used while serving a request.

(defgeneric service (relevant-object context)
  (:documentation "The core request handling generic function.

The service protocol consists of 4 passes: application -> session
-> session-frame -> action. At each phase the appropriate object
is passed to service with the current request object (which is
also bound to *context*). The appropiate object must perform
whatever it needs and then explicitly call service again passing
it the next object in the chain."))
(defvar *context* :unbound
  "The current request-context object.")

Sessions 

(defclass session ()
  ())
(defgeneric session.id (session)
  (:documentation "Returns the id of SESSION.

An ID is a unique (within the same application) object
identifying SESSION."))
(defgeneric expiredp (session)
  (:documentation "Returns true if SESSION has expired."))
(defgeneric expire-session (session)
  (:documentation "Causes this session to cease to exist."))
(defgeneric make-new-frame (session)
  (:documentation "Adds a new session-frame object to SESSION."))
(defgeneric session.current-frame (session)
  (:documentation "Return the current active frame in the session."))
(defgeneric session.value (key session &optional default)
  (:documentation "Fetch the value with key KEY in the session SESSION.

DEFAULT is returned should KEY not be found in SESSION's
session-table.

Keys are compared using EQL."))
(defgeneric (setf session.value) (value key session &optional default)
  (:documentation "Sets the value of KEY in SESSION to VALUE.

The DEFAULT argument is required to maintain the gethash-ish
interface and to allow for the use of this function in incf and
push macros.

As with session.value keys are compared with EQL."))
(defun get-session-value (key
			  &optional
			  (default nil)
			  (session (context.session *context*)))
  "Convenience wrapper around session.value.

SESSION defaults to the current session."
  (session.value key session default))
(defun (setf get-session-value) (value key
				 &optional
				 (default nil)
				 (session (context.session *context*)))
  "Convience wrapper around (setf session.value).

SESSION defaults to the current session."
  (setf (session.value key session default) value))

Session Frames 

(defclass session-frame ()
  ()
  (:documentation "Super class of all session frame.

A session frame represents a single request/response interaction
with a client. Session frames know what actions they've generated
and what the component tree was when the response was sent."))
(defgeneric frame.id (session-frame))
(defgeneric frame.window-component (session-frame)
  (:documentation "Accessor for SESSION-FRAME's window component."))
(defgeneric (setf frame.window-component) (component session-frame))
(defgeneric call-callback (session-frame param-name value)
  (:documentation "Call the parameter callback in SESSION-FRAME
with name PARAM-NAME passing it VALUE. PARAM-NAME is always a
string, VALUE is either a string or a list of strings depending
on how many values where under the name PARAM-NAME."))
(defgeneric find-action (session-frame action-id)
  (:documentation "Return the action (a function) associated with ACTION-ID."))
(defgeneric make-next-frame (frame new-frame-id)
  (:documentation "Create the \"next\" frame object with id NEW-FRAME-ID.

The new frame object must build its history (component,
backtracks, etc.) assuming that FRAME is the previous (in
chronological order) frame."))

Entry Points 

NB: actions are just regular methods and so don't explicitly appear in this protocol.

(defclass entry-point ()
  ()
  (:documentation "Generic super class of all entry-points.

Entry points are the means by which a user begins their
interaction with a UCW application. 

An entry point is an action (a function) associated to a
particular URL in an application."))
(defgeneric register-entry-point (object url entry-point)
  (:documentation "Inform OBJECT (either an application, a server
  or a backend) to start handling requests for ENTRY-POINT at
  URL."))
(defgeneric unregister-entry-point (object url entry-point))
(defgeneric entry-point.url (entry-point)
  (:documentation "Returns the query path associated with ENTRY-POINT.

The returned query-path will need to be combined with the
application's url-prefix to produce a complete query-path."))
(defgeneric entry-point.action (entry-point)
  (:documentation "Returns the function which should handle
  requests for the entry point."))