ToC DocOverview CGDoc RelNotes Index PermutedIndex
Allegro CL version 11.0

Using Allegro Webactions

This is the Webactions tutorial documentation as of the release of Allegro CL 7.0 (in October, 2004). It is provided for convenience but the Webactions documentation is regularly updated. We recommend that users use the latest version, available at http://opensource.franz.com/aserve/aserve-dist/webactions/doc/using-webactions.html. That version is always the most up-to-date. AllegroServe, including Webactions, is available from http://opensource.franz.com/. The Webactions manual is webactions.html. See also the AllegroServe documentation aserve.html.

Introduction

The web sites of the past began as a way to display static data. Today's web sites, however are graphical user interfaces to services or collections of data.  They are no longer simply web sites, but rather web applications.  From banking tasks to airline reservations to news sites, consumers are accessing content-specific data via an internet browser. 

Creating a web application is, in many ways, more difficult than creating a standalone application -- and it will become even more so as users demand increasingly customized features/services.  This paper will discuss some of the key challenges web application developers face, and how Allegro Webactions solves these problems.   Allegro Webactions sits on top of AllegroServe (Franz Inc.'s Lisp-based web server) and  provides a framework for building  web applications that are easy to maintain and update.    


Skyscrapers can't be built on the foundation of a Log Cabin

Customers are continuously demanding more, better, and faster services from web applications. Unfortunately, many developers are still attempting to layer dynamic features on top of a static structure.  In order to successfully cater to user demands, we must change the paradigm of how web sites are built and structured.  What worked for a 10 page web site two years ago, will not work for a 100 page site that includes numerous databases and an e-commerce application.  Some key challenges that need to be addressed include the following:

SessionControl

The web server must track individual visitors as they navigate through the web site.   The first time a visitor comes to the web site the server assigns that visitor a session object.   Subsequent requests by the same visitor must be assigned the same session object so that the code processing the request does work for that specific visitor.  For example a  visitor entering a shopping site is assigned a virtual shopping cart.  As the visitor adds items to the cart he expects to see previously added items still in the cart.   Without a session object the visitor would find that his shopping cart was always empty which, needless to say, would destroy the concept of online shopping.

The http protocol on which the web is built is a connectionless protocol.   A visitor does not connect to a web site and stay connected until moving to another web site.   Thus there needs to be some means for each request to identify the visitor behind the request.   Cookies were designed for this purpose.  A web server can ask a web browser to store a value on the visitor's machine that will be returned to the web server on subsequent requests.  

Cookies are a great tool for tracking sessions however some visitors disable cookies due to privacy concerns.  Therefore, a web application must be prepared to fall back on other methods for tracking sessions.    

Web Designer vs. Programmer

Building a web application requires two types of talent: a web page designer and a programmer.    Once the web site is up and running the daily maintenance can be done by the web page designer.   If significant new functionality is needed the programmer may have to return to write new code.

A successful web application framework should allow for the web site to be modified by either the designer or the programmer, depending on the task.  Requiring a programmer to be involved every time a product or text change occurs is inefficient and expensive. 

Dynamic Site Enhancement

Web sites evolve constantly -- some even need to be updated hourly.   And, if a site is being accessed many times a second, the site can't go down while these modifications are made.  A site's framework must support changing the static content and the dynamic content generation functions.    

Dealing With Complexity

A web application usually consists of a large set of pages with various links between them.   When new features are added, new pages are also added, which cause links to be added or modified.  Plus, there are often several paths through the web site that must yield equivalent results.  For example, in a shopping site a visitor can just start adding items to the shopping cart, and then identify himself at check-out.  Alternatively, a visitor can first identify himself and then start adding items to his cart.  In either case, the same products are ordered..   

Many web sites are like unstructured programs with "goto's" all over the place. As more products and features are added, the complexity can become overwhelming -- the site becomes slower, more brittle, and less robust with each change.  Something has to be done to organize and simplify the process.



Model View Controller

Suppose a web site is selling clothing.    A user sees a nice sweater and clicks on the button to add that sweater to his cart.  The webserver gets the request.  How should it handle it?

One way would be to write a function that identifies the user sending the request, adds the sweater to that user's shopping cart, and then writes out a page showing the current contents of the cart.   This type of monolithic function approach works, but is not very flexible.  Perhaps a change needs to be made that requires an inventory check first, and then displays a different message if the item is not in inventory.  That would make the programmer have to rewrite the monolithic function.   Maybe the cart contents need to be displayed from a link on every store page.  Since the cart displayer code is in the handler function we just described, the code must be duplicated or pulled out to be a separate routine.    In the monolithic function approach, the logic to choose what to display next is intermixed with the code to manipulate the store database and to display store items.   This makes it very hard to figure out all the links through the web site.

A better way to design a web application is to separate out these three types of code:

Using the MVC paradigm when the user asks to add a sweater to his cart  a Model function is called.   The Model function manipulates the store's databases to add the sweater to the user's cart.    The View function then displays the cart.

 

A Flexible Foundation

The paradigm described above, can easily be executed using Allegro WebActions and AllegroServe.  Although there exist a number of web application frameworks, Webactions' simplicity, transparency and power distinguish it from other tools.   Allegro WebActions uses Lisp as the web extension language.   Lisp is not a simple scripting language (like Perl or PHP) or a byte interpreted strongly-typed language (like Java).   Lisp is a flexible runtime-typed language that compiles down to machine code.   As a result, code written in the Lisp extension language runs at machine speed with no interpreter overhead.

Webactions borrows ideas from the Struts web application framework for Java.    Struts has good ideas for mapping web applications into the Model-View-Controller paradigm (more on this below).     Due to the more static design of the Java language, Struts is not as easy to use as Webactions.

Webactions components

Unlike many other tools, which were cobbled together as web site requirements increased, Allegro WebActions was designed specifically to support complex web applications.  It can easily support the key challenges outlined in this paper:

Session Control

Webactions supports  url rewriting in addition to Cookies.  In url rewriting the links to other pages in the site are modified to include a session identifier.    Webactions automatically does the check for disabled cookies and then switches to url rewriting, if necessary.

Webactions is also careful to create session identifiers that are unique and virtually impossible to guess.  This prevents a malicious user from guessing a valid session identifier and hijacking another user's session.  

Separation of Text and Code

Webactions provides a clear distinction between web design and programming, allowing web designers to work independently with their choice of tools.   Webactions introduces  a  syntax of special tags that display dynamic content in static pages.   This syntax was designed specifically so that  existing "What you see is what you get" (WYSIWYG) html editors  such as FrontPage and Mozilla will accept them.     Webactions does not permit programming constructs to appear in html pages, so web designers won't see code they don't understand.    This is not to say that Webactions forbids other scripting languages (such as javascript) from appearing in web pages.  Webactions simply does not add its own set of programming constructs to html (as Java Server Pages do for example).

Dynamic Updates

Webactions automatically notices when static pages are updated on the disk and begins serving them immediately.    One can load in new definitions of dynamic functions and have them in use right away (this is a benefit of all Lisp programs).

Complexity

Webactions adds a layer of abstraction which greatly simplifies the code.   Web pages are denoted by symbolic names.   Links from one web page to another are made using the symbolic name, as well.    There is a single project description that lists all symbolic page names and describes how each is rendered.     This project description allows a programmer to see a complete overview of the web site.   Also, symbolic page names can be changed without having to edit any file that refers to it. 


Components of an Allegro WebActions Web Site

  1. Project Description - Describes the pages in the project and where static pages are found and gives some parameters values.  The project description can be modified and loaded in while the web site is running, thus allowing the site to change dynamically (in true Lisp fashion).
  2. Static Pages -Created by the web designer, they can be changed at any time.  The next time an updated or new page is referenced, the new value will be used.
  3. clp-functions - these are Lisp functions that display html (generally these are used to show the dynamic parts of pages).
  4. Action functions - these are Lisp functions that interact with the application behind the web site (e.g. the store).

In MVC terms, the project description is input to the Controller.  Items 2 and 3 are View components.  The Model functions are item 4.  Allegro Webactions itself implements the Controller.

Simple Example

Let's examine a very simple Webactions web site.   The project description is:

(webaction-project "simpleproject"
		   :destination "site/"
		   :index "home"
		   :map
		   '(("home"   "pageone.clp")
		     ("second" "pagetwo.clp")))

Let's skip right to the :map argument.   Its value is a list of the pages that make up this project.   Each page is described by its symbolic page name followed by an object  which describes how the  page is to be rendered.  In this example we have two pages, one named "home" and the other "second".     The render value for each of these pages is a string which in Webactions means that each is rendered by serving a file on disk with the given name.

There are two files that accompany this project description:

site/pageone.clp:

<html>
<body>
This is page one.
<br>
Go to <a href="second">page two<a>
</body>
</html>


site/pagetwo.clp:

<html>
<body>
This is page two.
<br>
Go to <a href="home">page one<a>
</body>
</html>


You'll note that the clp files look just like html files.  This is intentional.   A clp file is just an html file that is processed by Webactions before being sent to the browser. Two types of  processing are done by Webactions.  First all symbolic page references are replaced by page references that work in the current context.   Symbolic page references are found in two places:  href="xxx" inside an <a> element and  action="xxx" inside a <form> element.   Second if there are references in the clp file to special clp Lisp functions, then those functions are called at the appropriate time when the page is being sent to the browser.

In our example above each clp page has one symbolic page reference.   Suppose we load Webactions and this project definition into Lisp and start the server on port 8000.  We go to a web browser and ask for http://localhost:8000/.
What happens is the url in the web browser changes to http://localhost:8000/home and we see:

This is page one.
Go to page two.


The reason the url changed to "/home" is that in the project definition we specified "home" as the index page for this project.  Thus accessing the project without specifying a particular page resulted in the request being redirected to the index page.

We click on the "page two" link and the browser now shows a url like:

http://localhost:8000/~159c546f07540c5a9ee4155d~/pagetwo.clp


and we see in the main frame of the web browser:

This is page two.
Go to page one.


Why does the url look so strange?  The reason is that when the first page (the page named "home") was processed by Webactions and sent back to the browser, Webactions didn't know if the browser would accept a cookie.   What Webactions did was send a cookie back with the page and at the same time it put the session identifier in the url for all symbolic page references.   The number 159c546f07540c5a9ee4155d is the session id chosen by Webactions.    When you clicked on page two this caused the web browser to send a request for that page to Webactions.   That request either arrived at the web server with cookie value or it did not.   If the request came with the cookie value then Webactions notes that for this session Webactions can depend on the cookie value being sent with each request and thus Webactions will not put the session id in any more urls.  If the cookie value wasn't sent then Webactions notes that it will have to alter urls for this session and Webactions no longer tries to send cookies for this session.

If you run this simple example twice, once with your browser set to accept cookies and once with it set to  not accept cookies you can see how Webactions adapts to your browser setting and still maintains session identity.

This simple two web page example doesn't do anything that couldn't have been done with two static html pages.    Even though this example doesn't make use of it, Webactions is maintaining session information while the pages of this example are accessed.   We'll extend the example to make use of session tracking by counting the number of times pages in this session were accessed.  


Adding a clp function

We'll change the two clp files to add a line showing the hit count:

site/pageone.clp

<html>
<body>
This is page one.
<br>
Go to <a href="second">page two<a>
<br>
session hits: <sample_hitcount/>
</body>
</html>

site/pagetwo.clp


<html>
<body>
This is page two.
<br>
Go to <a href="home">page one<a>
<br>
session hits: <sample_hitcount/>
</body>
</html>

and then we define  a clp function in this way and load it into the Lisp running the web server:

(def-clp-function sample_hitcount (req ent args body)
  (let ((session (websession-from-req req)))
    (net.html.generator:html 
     (:princ
      (setf (websession-variable session "hitcount")
	(1+ (or (websession-variable session "hitcount") 0)))))))
    

Now we again go to a browser and view the pages.   Now we see pages like this:

This is page one.
Go to page two.
session hits: 9

What is happening now is that when Webactions returns pageone.clp it sends all of the page up to but not including <sample_hitcount/> to the browser, then it runs the sample_hitcount clp function, and then it sends the contents of  pageone.clp after <sample_hitcount/> to the browser.    The sample_hitcount function retrieves the session object associated with this request (using websession-from-req) and uses this session object to increment the session variable named "hitcount".   You're free to define as many session variables as you wish.  If you access a session variable that hasn't been set, the value nil is returned.   sample_hitcount increments the "hitcount" variable for this session and then prints it to the html stream.  

You may have noticed in the clp files that we used an xhtml syntax when we wrote html code to invoke sample_hitcount:

<sample_hitcount/>

We could have written the equivalent

<sample_hitcount></sample_hitcount>

instead but the former is easier to type.   While some html elements don't have a body (e.g. <img>) all clp elements we add to html have a body and the end of the body must be denoted in one of the two above ways.


Actions

The previous examples show the use of static clp pages and the introduction of dynamic content via clp functions.   There is one important part of Webactions left to describe and that is Actions.    When a web page is referenced symbolically it can invoke a lisp function to perform some action.  Usually that action affects the data object behind the web site or the current session object.   After the action is performed an invisible redirect is done to another page on the web site which is then handled either by another action or by displaying a web page.
Actions should never send anything to the web browser.  The sole function of an action is to affect the state of the Model behind the web site for this particular session.

Our example for the use of actions is something found in most dynamic web sites these days:  the login page.  In our sample web site we want to know the name of the person visiting our site so we can personalize the page.    The user enters the web site at the main entry point  and we check to see if he has logged on yet.  If so we go right to the home page of the site.   If he hasn't then we ask him to identify himself and once that's done we go to the home page of the site.

We begin with the project description and the two actions referenced:

(webaction-project "simpleproject"
		   :destination "site/"
		   :index "home"
		   :map
		   '(("home"   action-check-login)
		     ("login" "login.clp")
		     ("gotlogin" action-got-login)
		     ("realhome" "home.clp")))




(defun action-check-login (req ent)
  (let ((session (websession-from-req req)))
    (let ((user (websession-variable session "username")))
      (if* user
	 then ; already logged in
	      ; just go to the real home
	      "realhome"
	 else ; must login
	      "login"))))

(defun action-got-login (req ent)
  (let ((username (request-query-value "username" req)))
    (if* (and username (> (length username) 0))
       then (setf (websession-variable (websession-from-req req) "user")
	      username)
	    "realhome"
       else "login")))


We have four symbolic pages named in this project.   Two of them refer to clp files we'll show below.   Two others refer to lisp functions that we call action functions.    Users coming to the site are redirected to the page with symbolic name "home".  That causes action-check-login to be called.  The action-check-login function checks to see if this session has a non-nil value for the session variable "user".  If so the user has already logged in.    Action functions never send anything back to the browser.  They simply return a string which is the symbolic name of the page in the project that should be processed next.    This action function returns either "realhome" or "login".     Both of those symbolic page names refer to clp files in the project description.

site/login.clp

<html>
<body>
What is your name: 
<form action="gotlogin">
<input type="text" name="username">
<input type="submit">
</form>
</body>
</html>

site/home.clp

<html>
<body>
Welcome to my page, <clp_value name="user" session/>.
</body>
</html>

The login.clp file puts up a form that asks the visitor to enter his name.   When the submit button is pressed control goes to symbolic page name "gotlogin".   In our project,  symbolic page name "gotlogin" is handled by action function  action-got-login shown above.    This action function reads the user name from the form and if it's non-empty it stores  the user name in the session variable named "user".     action-got-login returns either symbolic page name "realhome" (if a valid user name was given) or "login" if a name wasn't given and if the visitor must try again to identify himself.
The symbolic page "realhome" is connected to the file home.clp.  This is a very simple home page that simply welcomes the user by name.  The clp_value function is part of the built-in Webactions library.  It retrieves and prints the value of a variable.   Here the variable name is "user" and the context is "session".  

Further Information

The Allegro Webactions document is a reference manual for clp pages and Webactions.

Summary

Allegro WebActions is a dynamic framework for building a web application.  Webactions does the work necessary to track sessions whether or not the browser accepts cookies and allows the visual part of the website to be designed by html programmers using tools they are accustomed to using.  The dynamic part of the web site is clearly partitioned from the static part so that the programming behind the web site will not interfere with the visual part. Allegro WebActions makes it easier to structure and update any web application in a cleaner, simpler way than with other current web application building tools.