Lisplets:彌合 Lisp 與 Java Servlets 之間的鴻溝

在 2000 年代初期,想要開發 Web 應用程式的 Lisp 開發者常面臨兩難:要麼重新發明輪子——自行編寫 HTTP 伺服器與 SSL 處理程式,要麼犧牲語言的彈性。於是 Lisplets 應運而生,這個於 2004 年由 Rich Hickey 創建的專案,旨在提供 Lisp 友善的 Java Servlets 介面。

Lisplets 如同一座橋樑,讓開發者能利用基於 Java 的 Web 環境(如 Tomcat、Jetty 或 Resin)的工業級功能,同時以 Common Lisp 或 Scheme 撰寫實際的應用程式邏輯。透過將 Web 的「管線」工作交給 Java,Lisp 開發者即可專注於語言的表達力,而不必犧牲可擴展性或安全性。

Lisplets 的運作方式

在核心上,Lisplets 由一個實作 HttpServlet 的單一 Java 類別組成。它不在內部處理請求,而是將請求轉發並使用 s-expression 透過 TCP socket 取得回應。

請求流程

  1. Interception: Java servlet 容器收到 HTTP 請求。
  2. Packaging: Lisplet servlet 將請求標頭、參數、Cookie、session 資料、應用程式狀態與使用者資訊封裝成單一 s-expression。
  3. Transmission: 此 s-expression 透過 TCP socket 傳送至監聽中的 Lisp 程序。
  4. Processing: Lisp 程序使用標準 read 函式讀取 s-expression,處理邏輯並產生回應。
  5. Return: Lisp 程序回傳包含狀態、標頭與 session 狀態的 s-expression,接著是原始文件內容。

對 Lisp 開發者的主要優勢

透過使用 Java servlet 容器作為前端,Lisplets 帶來多項即時的好處:

  • Infrastructure for Free: 開發者可取得專業的 HTTP 伺服器實作、SSL 支援,以及與業界標準 Web 伺服器的整合,且無需撰寫任何 Java 程式碼。
  • Enterprise Features: Lisplet API 完整呈現 Servlet API 的功能,包括 session 管理、驗證、存取控制、使用者角色與宣告式設定。
  • Hybrid Applications: 由於它在標準的 Java Web 應用程式內運作,Lisp 產生的內容可與 Java Server Pages (JSP) 或其他 Java Servlets 共同存在於同一應用程式中。
  • Flexible Deployment: 它可在任何 servlet 容器上執行,讓開發者可依成本、嵌入性或平台支援等因素選擇環境。

技術深度探討:API

透過 web.xml 進行設定

Lisplets 透過標準的 web.xml 檔案進行設定。lisp-hostlisp-port 為必填項,用以定位 Lisp 程序;此外還有多個可選參數供微調:

  • lisp-tag-prefix: 允許開發者在標籤前加上字串(例如 :),從而在 Common Lisp 中將其轉為關鍵字。
  • lisp-create-session: 布林值,決定若不存在 session 時是否自動建立。
  • lisp-roles: 以逗號分隔的清單,用於查詢使用者是否屬於特定角色,將 Lisp 邏輯與 Java 的驗證系統結合。

請求與回應格式

請求以單一的列表之列表形式傳送。每個子列表以標籤開頭(例如 :method:uri:headers:session)。此結構讓 Lisp 程序能以標準的列表處理技巧輕鬆解析請求。

回應的結構亦相同。Lisp 程序必須至少傳送一個空列表 () 給 Java 端。回應的 s-expression 可控制:

  • HTTP Status: 設定狀態(例如 200 OK)或觸發重新導向。
  • Cookies: 定義名稱、值、域名與路徑。
  • Session/Context Attributes: 更新 Java 端的動態屬性,並由 servlet 容器持久化。
  • Logging: 直接將字串送至 ServletContext 日誌。

實作範例

Rich Hickey 提供了一個為 Xanalys(LispWorks)編寫的示範伺服器,以展示整合的簡易性。該伺服器監聽一個埠口,從串流讀取 s-expression,並將請求回傳給瀏覽器作為回應主體:

(defun handle-lisplet-on-stream (stream)
  (unwind-protect
      (let ((req (read stream)))
        (format stream "~S"
                `((:status 200)
                  (:headers (:content-type "text/plain"))
                  (:log "Hello from Lisp")))
        (pprint req stream)
        (force-output stream)
        (close stream))))

摘要

Lisplets 提供了一個優雅的解決方案,以應對經典問題:如何在使用高階且彈性的語言撰寫邏輯的同時,利用穩定且高效的基礎設施生態系。雖然此專案始於 2004 年,但其透過簡單、語言無關的通訊格式(s-expression)來橋接兩種不同執行環境的理念,仍是一個永恆的架構模式。

Sources