Chapter 4. Application Initialization & Configuration
4.1. Introduction
The initialization and configuration of applications deployed to Immutant can potentially involve multiple files:
- the deployment descriptor (if used)
- the Leiningen project.clj (if present at the application root)
- the immutant.init namespace (if present beneath
src/)
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:
- If the deployment is a descriptor file, this file is loaded in a
global Clojure runtime to parse the map from the file. The
:rootentry 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. - If the deployment is an archive, or the
:rootfrom the descriptor points to an archive, the archive is expanded on disk to a temporary directory. - If a
.immutant.cljis 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. - Once the application root has been determined, Immutant looks for
a Leiningen
project.cljin the root. If present, the project file is loaded and if an:immutantkey is preset in the resultingproject, 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 theprojectvia Maven (this behavior can be overridden - see :resolve-dependencies). - 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). - 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.
- If an initialization function was specified in either the
descriptor or the
:immutantmap in the project (via the:initkey), 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 animmutant.initnamespace. 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.:rootis only valid in a deployment descriptor, and is ignored if specified inproject.clj.:resolve-dependencies- determines if dependencies defined inproject.cljwill be resolved via Pomegranate. This resolution process downloads and installs any missing dependencies to$M2_REPO(~/.m2/repositoryby default). If not specified,:resolve-dependenciesdefaults 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 loadingproject.clj. If no profiles are specified, only the:dev,:base, and:userprofiles 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-portor: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.