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. 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:

  1. 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.
  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 present 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 Pomegranate (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, each with an effective classpath based on the application's dependencies.
  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. If no init function was specified, Immutant will attempt to load an immutant.init namespace. Lacking that, the lein-ring plugin's configuration in project.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 in project.clj or .immutant.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.
  • :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.

Immutant 1.1.1