2 Stateful Servlets
A stateful servlet should provide the following exports:
This indicates that the servlet is a version two servlet.
(start initial-request) → can-be-response? |
initial-request : request? |
This function is called when an instance of this servlet is started.
The argument is the HTTP request that initiated the instance.
An example version 2 module:
These servlets have an extensive API available to them: net/url, web-server/http,
web-server/http/bindings, web-server/servlet/servlet-structs, web-server/servlet/web,
web-server/servlet/web-cells, and web-server/dispatch.
Some of these are documented in the subsections that follow.
2.2 Responses
Servlets communicate to the Web Server by returning HTTP responses. In order to
accomodate lightweight programs (and backwards compatibility), the Web Server
provides an indirection from application-specific response formats and the internal
HTTP response format,
response.
any->response coerces any value into a response or returns
#f if coercion is not possible.
any->response guarantees that any
response? input must always be returned exactly (i.e.
eq?.)
The default always returns
#f, signifying that no coercion is possible.
set-any->response! replaces the global
any->response with the supplied argument. This
function should return the same value for
eq? inputs to ensure that
can-be-response? is
any accurate predicate. Similarly, this function should be cheap to call multiple times on the same input,
since it will be used in contract checking as well as coercion before transmission. You may want to use a
weak
eq?-based hash-table to cache the results for this purpose. (See
make-weak-hasheq.)
2.3 Web Interaction
Sends response to the client. No continuation is captured, so the servlet is done.
Captures the current continuation, stores it with
exp as the expiration
handler, and binds it to a URL.
make-response is called with this URL and
is expected to generate a
can-be-response?, which is sent to the client. If the
continuation URL is invoked, the captured continuation is invoked and the request is
returned from this call to
send/suspend.
Example:
(send/suspend |
(lambda (k-url) |
(response/xexpr |
`(html (head (title "Enter a number")) |
(body |
(form ([action ,k-url]) |
"Enter a number: " |
(input ([name "number"])) |
(input ([type "submit"])))))))) |
When this form is submitted by the browser, the request will be sent to the URL generated by
send/suspend.
Thus, the request will be “returned” from
send/suspend to the continuation of this call.
Use
send/suspend/dispatch when there are multiple `logical’ continuations of a page.
For example, we could either add to a number or subtract from it:
Notice that in this example the result of the handlers are returned to the continuation of
send/suspend/dispatch.
However, it is very common that the return value of
send/suspend/dispatch is irrevelant in
your application and you may think of it as “embedding” value-less callbacks. Here is the same example in this style:
Use this if the user can logically go `forward’ in your application, but cannot go backward.
Use this if the user is truly `done’ with your application. For example, it may be used to display the post-logout page:
(send/finish |
(response/xexpr |
`(html (head (title "Logged out")) |
(body (p "Thank you for using the services " |
"of the Add Two Numbers, Inc."))))) |
This implements the Post-Redirect-Get pattern.
Use this to prevent the Refresh button from duplicating effects, such as adding items to a database.
Holds the expiration handler to be used when a continuation
captured in this context is expired, then looked up.
Calls the servlet’s manager’s
clear-continuation-table! function. Normally, this deletes all the previously
captured continuations.
Calls thunk with an exception handler that generates an HTML error page
and calls send/finish-or-back.
Warning: This is deprecated and will be removed in a future release.
Checks if u is a URL that refers to a continuation, if so
returns the instance id, continuation id, and nonce.
The tag used for Web interaction continuation capture.
2.4 Web Cells
A Web cell is a kind of state defined relative to the frame tree.
The frame-tree is a mirror of the user’s browsing session. Every time a
continuation is invoked, a new frame (called the current frame) is
created as a child of the current frame when the continuation was captured.
You should use Web cells if you want an effect to be encapsulated in all
interactions linked from (in a transitive sense) the HTTP response being
generated. For more information on their semantics, consult the paper
"Interaction-Safe State for the Web".
Determines if v is a web-cell.
Creates a web-cell with a default value of v.
Looks up the value of wc found in the nearest
frame.
Binds wc to v in the current frame, shadowing any
other bindings to wc in the current frame.
Below is an extended example that demonstrates how Web cells allow
the creation of reusable Web abstractions without requiring global
transformations of the program into continuation or store passing style.
2.5 Continuation Managers
Since Racket servlets store their continuations on the server, they take
up memory on the server. Furthermore, garbage collection can not be used
to free this memory, because there are roots outside the system: users’
browsers, bookmarks, brains, and notebooks. Therefore, some other strategy
must be used if memory usage is to be controlled. This functionality is
pluggable through the manager interface.
2.5.1 General
This module defines the manager interface. It is required by
the users and implementers of managers.
create-instance is called to initialize a instance, to hold the
continuations of one servlet session. It is passed
a function to call when the instance is expired. It runs the id of the
instance.
adjust-timeout! is a to-be-deprecated function that takes an
instance-id and a number. It is specific to the timeout-based manager
and will be removed.
clear-continuations! expires all the continuations of an instance.
continuation-store! is given an instance-id, a continuation value,
and a function to include in the exception thrown if the continuation is
looked up and has been expired. The two numbers returned are a
continuation-id and a nonce.
continuation-lookup finds the continuation value associated with
the instance-id, continuation-id, and nonce triple it is given.
continuation-peek is identical to continuation-lookup except that
its use must not affect the resource management policy decisions on the instance or
continuation accessed. It is intended to be used by debuggers and benchmarks.
This exception should be thrown by a manager when an instance is looked
up that does not exist.
This exception should be thrown by a manager when a continuation is
looked up that does not exist.
2.5.2 No Continuations
This module defines a manager constructor:
This manager does not actually store any continuation or instance data.
You could use it if you know your servlet does not use the continuation
capturing functions and want the server to not allocate meta-data
structures for each instance.
If you do use a continuation capturing function, the continuation is
simply not stored. If the URL is visited, the instance-expiration-handler
is called with the request.
If you are considering using this manager, also consider using the
Web Language. (See
Stateless Servlets.)
2.5.3 Timeouts
This module defines a manager constructor:
Instances managed by this manager will be expired
instance-timeout
seconds after the last time it is accessed. If an expired instance is
looked up, the
exn:fail:servlet-manager:no-instance exception
is thrown with
instance-exp-handler as the expiration handler.
Continuations managed by this manager will be expired
continuation-timeout
seconds after the last time it is accessed. If an expired continuation is looked
up, the
exn:fail:servlet-manager:no-continuation exception
is thrown with
instance-exp-handler as the expiration handler, if
no expiration-handler was passed to
continuation-store!.
adjust-timeout! corresponds to reset-timer! on the timer
responsible for the servlet instance.
This manager has been found to be... problematic... in large-scale
deployments of the Web Server .
2.5.4 LRU
This module defines a manager constructor:
Instances managed by this manager will be expired if there are no
continuations associated with them, after the instance is unlocked.
If an expired instance is looked up, the
exn:fail:servlet-manager:no-instance exception
is thrown with
instance-exp-handler as the expiration handler.
Continuations managed by this manager are given a "Life Count" of
initial-count initially. If an expired continuation is looked
up, the
exn:fail:servlet-manager:no-continuation exception
is thrown with
instance-exp-handler as the expiration handler, if
no expiration-handler was passed to
continuation-store!.
Every check-interval seconds collect? is called to determine
if the collection routine should be run. Every collect-interval seconds
the collection routine is run.
Every time the collection routine runs, the "Life Count" of every
continuation is decremented by 1. If a continuation’s count
reaches 0, it is expired. The inform-p function
is called if any continuations are expired, with the number of
continuations expired.
The recommended usage of this manager is codified as the following function:
This creates an LRU manager with the following behavior:
The memory limit is set to memory-threshold bytes. Continuations start with 24
life points. Life points are deducted at the rate of one every 10 minutes, or one
every 5 seconds when the memory limit is exceeded. Hence the maximum life time for
a continuation is 4 hours, and the minimum is 2 minutes.
If the load on the server spikes—as indicated by memory usage—the server will quickly expire
continuations, until the memory is back under control. If the load
stays low, it will still efficiently expire old continuations.