JBoss.orgCommunity Documentation

Chapter 4. Application Initialization & Configuration

4.1. Introduction

The initialization and configuration of applications deployed to Immutant can potentially involve multiple files:

Each of these files have the opportunity to contribute to the configuration or initialization of the application.

4.2. The Initialization Process

When an Immutant detects a Clojure deployment (either a deployment descriptor or an Immutant archive), it begins the application initialization process:

  1. If the deployment is a descriptor file, this file is loaded in a global Clojure runtime to parse the map from the file. The :root entry from that map is then used to determine the actual root of the application, and can refer to the application directory or the path to an Immutant archive. Any additional configuration information from the descriptor is stored for later merging with data from other configuration sources.
  2. If the deployment is an archive, or the :root from the descriptor points to an archive, the archive is expanded on disk to a temporary directory.
  3. If a .immutant.clj is present in the root of the deployment, it is loaded and merged with the configuration from the descriptor, with the descriptor taking precedence. This file is used to relay configuration data captured when an archive is generated, and you generally shouldn't need to manipulate it directly.
  4. Once the application root has been determined, Immutant looks for a Leiningen project.clj in the root. If present, the project file is loaded and if an :immutant key is preset in the resulting project, it is merged with the configuration data from the descriptor with the configuration from the descriptor taking precedence. Leiningen is then used to resolve the dependencies present in the project via Maven (this behavior can be overridden - see :resolve-dependencies).
  5. If a lib/ directory is present in the application root, any jars within it are merged with the dependencies calculated in the previous step. Each of these jars are added to the isolated classpath for the application, along with source and resource directories within the application root (see Dependencies & Resources for more information).
  6. At this point, Immutant creates a Clojure runtime for the application. Runtimes in Immutant are isolated from each other, and can each load their own versions of Clojure or any other jar.
  7. If an initialization function was specified in either the descriptor or the :immutant map in the project (via the :init key), it is loaded and invoked within the application's runtime (see :init for more details). If no init function was specified, Immutant will attempt to load an immutant.init namespace. See immutant.init for more information on using this initialization method. At this point, the application is fully initialized.

4.3. Configuration Options

The following configuration options can appear in the deployment descriptor or (with the exception of :root) the :immutant map of project.clj or a .immutant.clj archive descriptor.

  • :root - specifies the path to the 'root' of the application. This can either be the path to the application directory, or the path to an Immutant archive. :root is only valid in a deployment descriptor, and is ignored if specified in project.clj.
  • :resolve-dependencies - determines if dependencies defined in project.clj will be resolved via Pomegranate. This resolution process downloads and installs any missing dependencies to $M2_REPO (~/.m2/repository by default). If not specified, :resolve-dependencies defaults to true. See Dependencies & Resources for more details on the dependency resolution process.
  • :lein-profiles - You can specify the Leiningen profiles (as a collection) to apply when loading project.clj. If no profiles are specified, only the :dev, :base, and :user profiles are loaded. For production usage, see the note below regarding profiles.
  • :swank-port & :nrepl-port - Immutant can provide a per-application repl, either via Swank or nREPL. Immutant will automatically start a repl for you if you specify an unused port using either :swank-port or :nrepl-port. See Interactive Development for more details.
  • :init - optionally specifies a function to call to initialize your application. It can be either a namespace-qualified symbol (my.app/do-something) or a string ("my.app/do-something"). Because the deployment descriptor is evaluated, it must be quoted (or a string) if present there.
  • :context-path - overrides the default web context path that is generated based on the deployment name. See Web - Context Paths for more details.
  • :virtual-host - allows setting a virtual host for the application. See Web - Virtual Host for more details.

A sample deployment descriptor:

;; :root is the only required option
{:root "/the/path/to/my/app"
 :resolve-dependecies false
 :lein-profiles [:dev :clj15]
 :swank-port 4242
 :nrepl-port 4343
 :init 'my.app/init
 :context-path "/"
 :virtual-host "foo.host"} ;; can also be: ["foo1.host" "foo2.host"]

4.3.1. Arbitrary Configuration Values

Since Immutant can host multiple apps, using system properties or environment variables may not be a practical way to provide per app configuration. In addition to the above options, you can put any values you need into the configuration within the deployment descriptor or the :immutant map of project.clj. This full configuration (including the above options) is available to your application by passing the :config key to immutant.registry/get:

(require '[immutant.registry :as registry])

(let [cfg (registry/get :config)]
  (:root cfg)              ;; "/the/path/to/my/app"
  (:init cfg)              ;; my.app/init
  (:my-custom-option cfg)) ;; :my-custom-value

If you need access to the leiningen project map for the application, it is available from immutant.registry/get under the :project key:

(require '[immutant.registry :as registry])

(registry/get :project)

4.3.2. A note on profiles

To ease the development process, the :dev, :base, and :user profiles are loaded by default. For production deployments, we strongly urge you to explicitly specify the profiles you want active, which will override the default set. Neither :base nor :user are appropriate profiles for production, since :base will bring in checkout dependencies, and :user will apply user level settings from ~/.lein/profiles.clj.

4.4. Files Used In Configuration & Initialization

4.4.1. Deployment Descriptor

A deployment descriptor is simply a Clojure source file containing a map of configuration options that is placed in Immutant's deployment directory to trigger the deployment of an application. It must contain a :root entry (see above), but any other configuration is optional. Any configuration specified here overrides configuration from project.clj. See the deployment descriptor for more details.

4.4.2. project.clj

project.clj is the standard configuration file used by the Leiningen project management tool. If a project.clj exists at the root of the application, Immutant will parse it and use it to determine dependencies and resource paths, as well as read Immutant specific configuration from the :immutant map. Here is a sample project.clj including all of the Immutant options that are usable in that file:

(defproject my-app "1.2.3"
            :dependencies [[org.clojure/clojure "1.3.0"]
                           [noir "1.2.0"]]
            :immutant {:init my-app.core/initialize
                       :resolve-dependencies true
                       :lein-profiles [:dev :clj15]
                       :context-path "/"
                       :virtual-host "foo.host" ;; can also be: ["foo1.host" "foo2.host"]
                       :swank-port 4111
                       :nrepl-port 4112})

4.4.3. immutant.init

immutant.init provides an alternate initialization mechanism to the :init function. If no :init function is specified, Immutant will attempt to load the immutant.init namespace. Typically, this namespace is used to start Immutant-specific services, like so:

(ns immutant.init
  (:require [immutant.daemons   :as daemons]
            [immutant.jobs      :as jobs]
            [immutant.messaging :as messaging]
            [immutant.web       :as web]
            [immutant.repl      :as repl]
            [immutant.util      :as util]
            [noir.server        :as server]
            [my-app.core        :as core]))

;; point noir to the right place for views
(server/load-views (util/app-relative "src/my_app/views"))

;; start a web endpoint
(web/start "/" (server/gen-handler {:mode :dev :ns 'my-app}))

;; spin up a repl
(repl/start-swank 4321)

;; schedule a job
(jobs/schedule "my-job" "*/5 * * * * ?" core/process-tps-reports)

;; start a daemon
(deamons/daemonize "my-daemon" core/daemon-start core/daemon-stop)

;; create a queue
(messaging/start "/queue/foo")

The services started in the above example are documented elsewhere in this manual.

4.5. Dependencies & Resources

Immutant generates a unique classpath for each deployed application based upon dependencies and source & resource directories defined in project.clj and any jars found in the lib/ at the application root.

The lib/ directory can be used to bundle dependencies with your application, and can be useful in the following situations:

  • you want to verify an application through a QA process without any external dependencies
  • you don't have the option of resolving dependencies at deploy time
  • you have dependencies that aren't published to a maven repository
  • your project doesn't use Leiningen

When dependency resolution is enabled for an application, Immutant uses Pomegranate (via leiningen-core) to resolve the dependencies against a local Maven repository. If the dependencies aren't available in the local repository, they are downloaded into it. The local repository is located based on the value of $M2_REPO, and defaults to ~/.m2/repository/. If this dependency resolution fails, none of the application's dependencies will be available from the local repository and must be be present elsewhere on the application's resource path (lib/ for example) to be accessible to the application.

The enablement of dependency resolution depends upon the value of the :resolve-dependencies option. Dependencies are resolved by default, but resolution can be disabled by setting :resolve-dependencies to false.

Immutant 0.10.0