JBoss.orgCommunity Documentation

Chapter 6. Immutant Web

6.1. Introduction

Immutant allows applications to respond to web requests via Ring handlers. Each application can dynamically register any number of handlers, each with a unique context path. This allows you to have multiple Ring webapps that share the same deployment lifecycle.

Immutant provides a session implementation that provides automatic data sharing across nodes in a cluster.

In this chapter, the term application refers to the full deployment itself, and webapp refers to a particular web application based around a single Ring handler as its entry point. For most applications, the two will be the same thing.

6.2. Context Path

The context path is a prefix on the path portion of a url that is used as a mechanism to route requests to the proper webapp when more than one webapp is being served by the same 'container'. If you are running only one webapp in a container (which is the typical strategy when deploying Clojure webapp via Jetty), the context path is the root context - /.

A properly constructed webapp needs no knowledge of the context path it is mounted under - the container is responsible for routing requests to the webapp and providing the webapp specific fragment of the url's path to it. This allows the webapp to be moved between contexts (or mounted at multiple contexts at once) without any modification to the webapp code itself.

6.2.1. The Top-Level Context Path

Every application deployed has a context path assigned on its behalf. Since handlers can be registered dynamically from anywhere during the lifecycle of the application, we reserve a context path for every application, whether that application registers web handlers or not.

If no context path is provided, a default context path based on the name of the deployment is used. For example: an application deployed using a descriptor named some-app.clj will be given the context path /some-app. An application deployed using an archive named some-other-app.ima will be given the context path /some-other-app. See Deployment for the details of deploying Clojure applications.

You can override the default context path via the :context-path key in either in the deployment descriptor or the application's project.clj. See Initialization for the details on setting configuration values.

This context path is considered the top-level context path - you have the option to bind a handler to a sub-context path that will be nested within the top-level path. The full context is stripped from the url's path before the request is processed, and the context and remaining path info are made available as part of the request map via the :context and :path-info keys, respectively.

6.2.2. The Sub-Context Path

When you register a handler with Immutant, you can optionally provide a sub-context path for which the handler will be responsible. This sub-context path is appended to the top-level context path for purposes of routing requests to the handler, and allows you to have multiple webapps within a single application.

6.3. Registering Handlers

To register a Ring handler, you simply call the immutant.web/start, which takes one or two arguments:

  • sub-context-path - the sub-context path within the application's context path where the handler should be attached. Optional - if omitted, "/" is assumed. Only one handler can be attached to any given sub-context path - providing an already attached sub-context will replace the previously registered handler.
  • handler - the Ring handler to invoke when requests come in on the sub context path.

Let's take a look at start in use. For the following example, assume the application has a top-level context path of /my-app:

(ns my.ns
  (:require [immutant.web :as web]))

;; handle requests at the root sub-context (/).
;; this handler will receive any request that the app
;; receives at /my-app/* *except* for anything captured by another
;; sub-context.
(web/start my-root-handler)

;; handle requests at the /somewhere sub-context.
;; this handler will receive any request that the app
;; receives at /my-app/somewhere/*.
(web/start "/somewhere" my-other-handler)

You can deregister a registered handler at any time. Immutant will deregister any remaining handlers for you when your application is undeployed.

6.4. Deregistering Handlers

You can deregister a Ring handler via the immutant.web/stop function, which takes zero or one arguments:

  • sub-context-path - the sub-context path within the application's context path where the handler was attached. Optional - if omitted, "/" is assumed.

An example of using stop:

(ns my.ns
  (:require [immutant.web :as web]))

;; deregisters the handler attached to the root sub-context (/)
(web/stop)

;; deregisters the handler attached to the /somewhere sub-context
(web/stop "/somewhere")

6.5. Sessions

Immutant provides a session store that can be used with the Ring session middleware (and any other middleware that uses ring.middleware.sesion, like Sandbar). The Immutant session store uses session provided by the underlying JBoss AS7 servlet container, which automatically replicates session data across a cluster. You create the store by calling immutant.web.session/servlet-store, and use it by passing it as the :store option to ring.middleware.session/wrap-session:

(ns my.ns
  (:require [ring.middleware.session :as ring-session]
            [immutant.web :as web]
            [immutant.web.session :as immutant-session]))

(web/start
 (ring-session/wrap-session
  my-handler
  {:store (immutant-session/servlet-store)}))

Note: since this store is managed by the servlet container, the session cookie (jsessionid by default) is itself managed at the servlet level. Any options other than :root passed to ring.middleware.session/wrap-session (:cookie-attrs, :cookie-name, or :root) will therefore be ignored.

6.6. src-dir

When a web server is embedded within an application, it's fine to make assumptions about relative paths because the current working directory for both the app and the web server is the same.

But this is not the case for Immutant, or any app server, because multiple applications may be deployed on it simultaneously. The app server is a single process, with a single current working directory, and an application should not assume that the server's current directory matches its root.

But certain libraries require actual filesystem paths to directories at runtime, and that's why immutant.web/src-dir exists: it returns the absolute path to a directory relative to the location of the file in which it's called. That sounds complicated enough for an example, right?

This won't work on Immutant because "src" is a relative path, and it will resolve relative to the server's working directory, not the app's:

(use 'ring.middleware.reload-modified)

(def app
  (wrap-reload-modified #'handler ["src"]))

But this will work on Immutant, because it returns an absolute path:

(def app
  (wrap-reload-modified #'handler [(web/src-dir)]))
Immutant 0.1.0