Having deployed a number of Web applications internally, we have found that each contained a number of similar features. We feel that these would be valuable additions to any web application. The features discussed below have been packaged into a web delivery framework that includes a simple server application and is available for download.
24/7 Availability The Web Service should always be available. This means running as a detached process (daemon) on *nix platforms, and as a service on windows platforms. On *nix platforms, there is a simple command-line option to detach the process. On windows, almost all of the command-line arguments exist to manage the creation/startup/removal of the service. See below for more details.
Live Updating of Content Adding new or modifying existing content should not require bringing down the webservice. Given a port on which the web application is intended to run, the framework also starts a reload server running on (+ port 1000) to allow for the refreshing of content.
Access to Lisp Environment Deployed applications should not be run from an environment tied to a tty or user login. A detached process and often an NT Service does not have an easily accessible console for debugging or inspection of the lisp environment. The framework provides access to a telnet server which runs on the loopback network interface at (+ port 1)
Logging Facility It is important that all requests be logged and that any potential standard IO be directed to file. The framework automatically logs all requests and problems handling requests to the files log/www_access and log/www_error. Standard output will also be redirected to file (on windows, it depends on the type of service). Lastly, a mechanism to support log rotation is established so that you can instruct the server when to close and reopen existing file streams.
Command-line Configurable The Framework contains sample command-line argument handling code that allows you to tweak the startup parameters of the Web application. On windows, you can register the server as a service to start on a given port, or unregister it. On non-windows platforms, you can run in debug or detached mode, depending on your needs. Command-line arguments are described further on in this document.
To install, simply extract the archive to your local filesystem. Once that's done, start up lisp and load the server framework. We include a small sample website that fetches weather data for select locales via ftp from the National Oceanic and Atmospheric Administration (NOAA) and displays it. To load the sample server into your development image, evaluate the following:
user(1): :cd /path/to/www-demo/ user(2): (load "load.cl")
To start the server simply call the function cl-user::init-server. It accepts a single numberic argument, specifying that port on which the server will listen. You will need to choose a port that is available and that the operating system will permit you to use. Web servers normally listen on port 80. On unix-like systems (macosx included) port 80 can only be allocated by the the superuser (called root). On windows any user can open port 80 as long as it's not yet allocated. In order to make this tutorial work on both Unix and Windows (and not require that you run as root on Unix), we'll put our web server on the localhost at port 8000. IDE and Express users should note that the default package when using the IDE is the :cg-user package, while this module uses the :cl-user package. This article will use full symbol names to facilitate your ability to cut and paste
user(3): (in-package :user) #<The common-lisp-user package> user(4): (cl-user::init-server 8000)
When this returns, you have a running server. Try visiting it from a web browser by navigating to http://localhost:8000/. You should see a weather report for Oakland, CA and San Francisco, CA.
Our sample weather portal works by using an ftp client to retrieve weather data from tgftp.news.noaa.gov. Each location is given a 4 letter identifier and is updated roughly hourly. A "Weather Monitor" process is started, which runs every hour (or on a forced reload) to update locally stored information about a particular region. We pass a variable containing a list of string identifiers, representing cities, to the weather monitor, for which it will maintain data. This variable can be updated through the publish.cl reload procedure, or by directly modifying it's value via the telnet service, and the new areas will be included in the servers output the next time the monitor updates or a reload request is made on the reload-server.
The web interface for the weather reporting can be found in src/publish.cl. The code for fetching each weather report can be seen in src/weather.cl
Now modify publish.cl. Reverse the comments on the following two lines at the end of the file and save it.
;; (defparameter *weather-sites* '("koak" "ksfo" "knuq" "kapa" "kbwi" "oakb" "ybrm")) (defparameter *weather-sites* '("koak" "ksfo"))
Now visit http://localhost:9000/reload. This forces a reload of publish.cl and should update the list of weather sites. When you visit http://localhost:8000/ again, you should see weather reports for a few other places around the world.
Additionally, you can modify the variable *weather-sites* by telnet'ing into the server and changing it directly. From a shell or your preferred telnet app, try to connect to the server:
% telnet localhost 8889 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. WARNING: do not use :exit or (exit). You will kill the web server if you do. Use (quit) to quit. cl-user(1): *weather-sites* ("koak" "ksfo") cl-user(2): (setq *weather-sites* '("koak" "ksfo" "knuq" "kapa" "kbwi")) ("koak" "ksfo" "knuq" "kapa" "kbwi") cl-user(3): (cl-user::quit) Connection closed by foreign host.
If the value of *weather-sites* does match the transcript above, you'll see a number of unknown places added to the output. This is because the "weather monitor" process has not yet fetched information for these locales from the weather service. You can issue a reload request to force the Monitor to update, or you can call a function to do so.
% telnet localhost 8889 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. WARNING: do not use :exit or (exit). You will kill the web server if you do. Use (quit) to quit. cl-user(1): *weather-sites* *weather-sites* ("koak" "ksfo" "knuq" "kapa" "kbwi") cl-user(2): (weather-monitor) (weather-monitor) nil cl-user(3): (quit) (quit) Connection closed by foreign host.
Another refresh of the main page http://localhost:8000/ and the unknown locations will now be known.
Generally, you will not run your server from a development image. Instead, you'll want to create a standalone web server. The file build.cl takes care of this. To create a standalone app, evaluate the following:
user(1): :cd /home/demo/www user(2): (load "build.cl")
Simple! Upon completion, there will be a dist/ subdirectory containing the runtime application. The binary executable to invoke the service is named sample-server. The command-line arguments differ between mswindows and non-mswindows versions and are detailed below.
The windows version of the framework makes use of the ntservice module available at http://opensource.franz.com/. You are encouraged to read the documentation for this module. In particular, the readme file has useful information about programs run under the "LocalSystem" account and how service behavior may differ on Windows Vista (which we include below).
Usage: sample-server --register [--interact-with-desktop] [--port port] [-o logfile] sample-server --unregister sample-server --start-service sample-server --stop-service sample-server [--port port] [-o logfile] [--interact-with-desktop] [-d]
Some sample invocations of sample-server would appear as follows:
;; register a service to run on port 8000, logging to sample-server.out % dist/sample-server --register --port 8000 -o sample-server.out ;; unregister the service % dist/sample-server --unregister ;; re-register to a different port, default logfile, interact with desktop % dist/sample-server --register --port 8888 --interact-with-desktop ;; start the service % dist/sample-server --start-service ;; stop the service % dist/sample-server --stop-service ;; debug mode! this will drop you into a toplevel break loop. % dist/sample-server --port 8000 -d
Usage: sample-server [-d] [-D] [-o logfile] port -d -- debugging mode -D -- daemon mode -o logfile -- log standard output to this file, or www.out if not given port -- the port on which we listen
Using the build application on unix is easy. Here are some sample invocations:
;; start a detached server running on port 8000 % dist/sample-server -D 8000 ;; start a detached server with output to sample-server.out % dist/sampler-server -D -o sample-server.out 8000 ;; start up in debug mode. You'll drop into a toplevel break loop. % dist/sample-server -d 8000
As always, we encourage you to examine the code. If you have any questions or problems with the application, please email us at firstname.lastname@example.org
|Copyright © 2014 Franz Inc., All Rights Reserved | Privacy Statement||