a web-framework using Armed Bear Common Lisp as a Java Servlet
Rationale Current Status License Structure Tutorial
The Armed Bear Common Lisp that works on top of Java Virtual Machine (JVM) can allow us to use the Common Lisp for web application development, and use all the mature Java web development platform features and libraries while programming in the dynamic and extensible Common Lisp language.
Typicaly Java web applications are implemented via the Java Servlets API.
Each servlet is a Java class, and servlets are executed in a Servlet Container, such as
Apache Tomcat. Thus, to Java web applications with
Common Lisp, we'd need to make a bridge between a Java Servlets and ABCL.
While there can be different approaches (for example, we can try to create servlets with ABCL), most simple and natural one is to create a Java Servlet, that will load ABCL and forward web requests to ABCL. ABCL will generate output for response to this requests. Such servlet will act as bridge between Java Servlet Container and the ABCL application side.
One can ask the question, why use Java if we have plenty of other Common Lisp implementations?
Well, plenty of Common Lisp implementations can be used for web development, but usage of Java
platform has its advantage. Most CL implementations do not have properly implemented
native mulithreading, while Java, and thus ABCL, have full and stable support for threads -- it
can make use of multiple cores/CPUs.
Maturity of CL web frameworks is questionable, while Java is very widespread and mature.
Also, a lot of libraries ara available for Java platform, and some of them might not be available for Common Lisp. While some of them can be used via FFI, it won't be feasible for functions that require high performance (FFI calls have their overhead)
There's also a question of hosting -- it might be easier to find Java hosting rather than Lisp web hosting. From hosting point of view, ABCL-web application is an ordinary Java Web Application, no installation of additional stuff is required.
Also, in some cases integration with existing solutions might be desired/required, or developers want have an option to easily migrate from Common Lisp to Java -- for example, they can code prototype in Common Lisp, and then migrate to Java for production high-performance system.
The current status is alpha. Working prototypes have been created and tested, but there are known issues,
this ABCL-web is not recommended for production environments.
The current project goal is to provide working prototype. The security have not being tested, thus installing ABCL-web on your webserver will open security holes, upto control from remote!
Current prototype might have problems installing into arbitrary Servlet Container, Servlet Container should be somewhat tuned -- security disabled, etc.
abcl-web is distributed under LGPL license, with clarifications from LLGPL applicable. (while components, such us ABCL itself, may come with different licenses). To clarify this: you can use this however you want with your web applications, but if you modify ABCL-web, we'd like to see your patches and apply them if they are useful in general.
Read Tutorial for further explanations.
The goal of this project is not just to enable some development possibilities, but a complete framework, so development will not only be possible, but will be effective, easy, productive and fun! Thus, we are going to provides all tools needed for development (if there are more than one way available, we are going to provide all ways for user to choose).
Thus, we divide abcl-web into following layers:
The Servlet Bridge passes requests from Servlet Container to the Lisp interpreter. It's also responsible to load the Lisp and initialize web application.
We also want to debug our code, thus we have two servlets -- LspExec and LspDebug. They are Java class files.
If we want debugging, we first launch LspDebug (you can run it on the Apache start). LspDebug loads the Lisp interpreter, and then loads file "lisp/dbgrstart.lisp", that is supposed to initialize a debugging interface. (since we use SLIME, dbgrstart.lisp loads and initializes the SLIME debugging server). dbgrstart.lisp is not critical to LspDebug -- it can work without it. Note, that unless you specify autostart, debugger will be loaded on first access to that servlet -- first access will take a long time, upto minute. Once loaded, LspDebug will either tell that it failed to load interpreter, or will provide a form where you can write the lisp code. It passes code you write to interpreter, and prints the results, or exception if code produces an error. Please note, that it's a very low-level debugger, and it's only intended to be used for diagnosing some weird bootstrap problems. Also, you should note that this debugger uses "throwing debugging hook" -- even if you'll load SLIME, it will report errors the same ugly -- but stable -- way. On the production servers, LspDebug should either be deleted, or access to it should be limited, since if you run it, anyone can execute arbitrary code on behalf of your webserver!.
LspExec is supposed to be loaded directly on production servers -- it's able to initialize
interpreter too, but it won't load dbgrstart.lisp though. Current version is able to detect
that Swank (SLIME backend) is initialized, and to use SLIME's debugging hook. If no swank is found,
any condition signaled will be critical and will be displayed instead of the page.
After installing hook, LspExec will try to load lisp/servlet-loader.lisp file. If servlet loading process will have some fixable problems, you'll be able to debug it in SLIME and continue process. When file is loaded successfully, it will try to find symbol CL-USER::*SERVLET*. If it finds that symbol, and it's a lisp function, initialization is considered successful.
As for requests, LspExec will either report problem (no interpreter or no servlet), or will pass request and response objects to that lisp function. Lisp function should use response object to do output. If lisp function throws, condition's class will be displayed.
web.xml file can be also considered a part of bridge, but it's quite simple. If your webapp is called lispwebapp, it will normally map LspDebug to http://myserver/lispwebapp/servlet/LspDebug and LspExec to http://myserver/lispwebapp/servlet/LspExec (you can change it). It should also automatically start LspDebug on debugging environment and LspExec on production.
The first level of loader consists of two lisp files -- dbgrstart.lisp and servlet-loader.lisp.
dbgrstart.lisp is used to to start a debugger -- SLIME, and initialize some parameters. Note that version of SLIME should be exactly same as you use in Emacs! Remote debugging is possible, but it requires some special tweaks.
servlet-loader.lisp is used to compile and load all required libraries and application code. For current version, any problem compiling/loading library is a critical error -- most likely you'll need to restart Servlet Container (tomcat) after such errors -- but that shouldn't worry you after you setup all required libs. The loading of application files (that are changed more frequently) is not a critical errror -- normally, you'll be able to debug it via SLIME, update your files, and restart loading process.
servlet-loader.lisp launches second-level initialization -- servlet.lisp file. Traditionally, servler.lisp creates packages, specifies Java classes being used and proceeds next-level initialization. We could use asdf for neat loading process, but so far we use simple loader, which allows retry loading each individual file. In current implementation, the servlet.lisp file also defines top-level servlet function -- servlet1-service, which calls analyze-request, process-request and generate-output functions, allowing restart at each level. Normally, web frameworks should just redefine those functions.
Currently the jfli-abcl library is used. There is an alternative, but jfli-abcl is faster.
You just specify Java class classes you want to use in the loader -- (def-java-class "java.io.InputStreamReader") -- and use package "java.io" unless you want to use qualified name |java.io|:InputStreamReader, and then you can create object -- (InputStreamReader.new some params), and call methods -- (InputStreamReader.read reader param pam pam) -- first parameter should be class itself. Normally you write all classes you use in the servlet.lisp, but you can also load new classes in REPL.
The roots of simple framework is defined in servlet.lisp -- they are functions analyze-request, process-request and generate-output. analyze-request should identify which request we are going to serve and to extract form data, user session and other parameters. Than both analyzed-request and normal request are passed to process-request. Process-request should perform business logic operations and produce some data for output. generate-output formats recieved data and sends them to response object. (i believe it's some kind of Dispatcher View pattern).
The goal of three-phase processing is that you can debug each part separately, so if generate-output is broken, you can have request parsed and processed, so you can just re-launch generate-output.
You can find that using simple framework for bigger projects would be overkill. We can offer you two flavours
of frameworks -- with auto-continuations and manual-continuations, more to come later :).
In current lispwebapp examples, web-engine -- 'manual-continuations' is used.
LML2 is supported/recommended.
(html (:p ((:a :href "http://lml2.b9.com/") "LML2") "is supported"))
(html (:p (:princ (+ 2 2) (dynamic-code-here))))
JDBC is accessible through jfli-abcl, you can connect to MySQL, PostgreSQL or whatever.
Alternatively, you can use Jena -- Semantic Web Framework -- and have semantic-level database! Here is a description of Lisp-friendly bindings.
CL-PPCRE regex lib is supported/recommended.