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 on the application's effective classpath)
Each of these files have the opportunity to contribute to the configuration or initialization of the application.
4.2. Porting an existing Ring application
People often have an existing Ring-based clojure project they'd like to
try on Immutant. If the project already uses the lein-ring plugin
and includes a :ring
key in project.clj
referencing a map with a
:handler
key, you're all set! Just deploy it!
However, projects that explicitly invoke run-jetty, typically from
some -main
function identified by a :main
key in project.clj,
will require porting. Immutant will ignore the :main
key, and
since Immutant includes a web server built in, running another Jetty
instance is redundant. But porting the app is simple: just add the
:ring
hash mentioned above, using the same handler passed to
run-jetty
. So if the app does this:
(run-jetty #'your.namespace/app)
Just add the following to project.clj
:
:ring {:handler your.namespace/app}
That's all you need to mount your handler on the default context
path when your app is deployed. See the web chapter for more
details, specifically the sections on context paths if your app
expects to be mounted at the root context, and handler registration
for additional options you can pass in the :ring
map.
4.3. 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 evaluated in
a global Clojure runtime, and must evaluate to a map. 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. - 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. - 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. - 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 present 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 theproject
via Pomegranate (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, each with an effective classpath based on the application's dependencies.
- 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. If no init function was specified, Immutant will attempt to load an immutant.init namespace. Lacking that, the lein-ring plugin's configuration inproject.clj
will be used, so any application configured for lein-ring should run in Immutant without any further changes. At this point, the application is fully initialized.
4.4. 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 inproject.clj
or.immutant.clj
.:resolve-dependencies
- determines if dependencies defined inproject.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 loadingproject.clj
. If no profiles are specified, only the:dev
,:base
, and:user
profiles are loaded. For production usage, see the note below regarding profiles.:nrepl-port
- Immutant can provide a per-application nREPL server by setting this option to an available port. In development mode, i.e. when the Leiningen:dev
profile is active,:nrepl-port
defaults to 0, making the nREPL server available on a port chosen by the OS, which is written to a file in your project directory known to many common nREPL clients. 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 either be a quoted symbol 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] :nrepl-port 4343 :init 'my.app/init :context-path "/" :virtual-host "foo.host"} ;; can also be: ["foo1.host" "foo2.host"]
4.4.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.4.2. A note on profiles and plugins
To ease the development process, the :dev
, :base
, and :user
profiles are loaded by default, along with any profiles specified
in ~/.lein/profiles.clj
and in the application's
project.clj
. 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.5. Files Used In Configuration & Initialization
4.5.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.5.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.5.1"] [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"] :nrepl-port 4112})
4.5.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-nrepl 4321) ;; schedule a job (jobs/schedule "my-job" core/process-tps-reports "*/5 * * * * ?") ;; 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.6. Dependencies & Resources
Immutant generates a unique effective 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.