Lisplets:弥合 Lisp 与 Java Servlet 之间的鸿沟

在 2000 年代初期,想要构建 Web 应用的 Lisp 开发者面临的挑战往往是要么重新发明轮子——自己编写 HTTP 服务器和 SSL 处理器,要么在语言的灵活性上妥协。于是 Lisplets 出现了,它是 Rich Hickey 于 2004 年创建的项目,旨在为 Java Servlet 提供 Lisp 友好的接口。

Lisplets 充当桥梁,使开发者能够利用基于 Java 的 Web 环境(如 Tomcat、Jetty 或 Resin)的工业级能力,同时在 Common Lisp 或 Scheme 中编写实际的应用逻辑。通过将 Web 的“管道”工作交给 Java,Lisp 开发者可以专注于语言的表达能力,而不必牺牲可扩展性或安全性。

Lisplets 工作原理

在核心上,Lisplets 由一个实现了 HttpServlet 的单一 Java 类组成。它不在内部处理请求,而是将请求转发并使用 基于 TCP 套接字的 s 表达式 收集响应。

请求流程

  1. 拦截:Java servlet 容器接收 HTTP 请求。
  2. 打包:Lisplet servlet 将请求头、参数、Cookie、会话数据、应用状态和用户信息打包成一个 s 表达式。
  3. 传输:该 s 表达式通过 TCP 套接字发送到监听的 Lisp 进程。
  4. 处理:Lisp 进程使用标准的 read 函数读取 s 表达式,处理逻辑并生成响应。
  5. 返回:Lisp 进程返回包含状态、头部和会话状态的 s 表达式,随后是原始文档内容。

对 Lisp 开发者的关键优势

通过将 Java servlet 容器用作前端,Lisplets 带来了若干直接的好处:

  • 免费基础设施:开发者无需编写任何 Java 代码即可使用专业的 HTTP 服务器实现、SSL 支持以及与行业标准 Web 服务器的集成。
  • 企业特性:Lisplet API 完全映射 Servlet API 的强大功能,包括会话管理、认证、访问控制、用户角色以及声明式配置。
  • 混合应用:由于它运行在标准的 Java Web 应用中,Lisp 生成的内容可以与 Java Server Pages(JSP)或其他 Java Servlet 共存于同一应用。
  • 灵活部署:它兼容任何 servlet 容器,开发者可以根据成本、可嵌入性或平台支持来选择运行环境。

技术深度解析:API

通过 web.xml 配置

Lisplets 通过标准的 web.xml 文件进行配置。lisp-hostlisp-port 是定位 Lisp 进程所必需的,而若干可选参数则用于微调:

  • lisp-tag-prefix:允许开发者在标签前添加字符串(例如 :),从而在 Common Lisp 中将其视为关键字。
  • lisp-create-session:布尔值,决定在会话不存在时是否自动创建会话。
  • lisp-roles:以逗号分隔的列表,用于查询用户是否属于特定角色,将 Lisp 逻辑与 Java 的认证系统集成。

请求与响应格式

请求以单个列表的列表形式发送。每个子列表以标签开头(例如 :method:uri:headers:session)。这种结构使 Lisp 进程能够使用标准的列表处理技术轻松解析请求。

响应的结构类似。Lisp 进程必须至少向 Java 端发送一个空列表 ()。响应的 s 表达式可以控制:

  • HTTP 状态:设置状态码(例如 200 OK)或触发重定向。
  • Cookie:定义名称、值、域和路径。
  • 会话/上下文属性:更新 Java 端的动态属性,随后由 servlet 容器持久化。
  • 日志记录:将字符串直接发送到 ServletContext 日志。

实现示例

Rich Hickey 提供了一个为 Xanalys(LispWorks)编写的示例服务器,以演示集成的简易性。服务器监听端口,从流中读取 s 表达式,并将请求回显为浏览器的响应体:

(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 表达式)来桥接两种不同运行时环境的理念仍然是一种永恒的架构模式。

Sources