“Give ordinary people the right tools, and they will design and build the most extraordinary things”
- Neil Gershenfeld, American Professor at MIT
Configuring your applications can be frustrating, painstaking and time-consuming. Different programs demand different forms of configuration - which necessitates tracking multiple configuration sources, verifying and establishing that they are existent, unifying them, and purging any and all incorrect syntax from each of them.
At FORMCEPT, verification of configurations is critical and of paramount importance. Over the last few years, we have worked with cutting edge technology to develop state-of-the-art applications with advanced algorithms. We need to make sure that the configuration is complete and accurate before the main code runs. A number configuration libraries exist for Clojure, such as: Environ, Aero, Nomad, Fluorine. However, most of them only perform specific functions, and do not include complete verification of code.
Introducing Omniconf
Omniconf is library for your Clojure programs wherein configuring applications is independent from configuration back-end.
Some of the important functions in Omniconf are explained below:
- omniconf.core/define - Configuration syntax must be defined before assigning the configuration values, and this function helps to define the same
- omniconf.core/get - Extracts the value of a set configuration
- omniconf.core/verify - Checks the defined configurations for any options that do not conform to the syntax provided. This way, errors can be detected and prevented before running the application. E.g. the file not found errors
How It Works
Omniconf is built on the following principles:
-
- Define in a single place, populate from many -
- The configurations expected by applications are defined at a single place.
- However, populating the configurations can be done from multiple places.
- Configurations can come from different sources ex: command line, environment variables, java properties, configuration file, etc.
- Access from anywhere in the application -
- Access all your configurations from anywhere in the application.
- Saves time and removes redundancy in accessing configurations
- All configuration sources must be merged -
- Irrespective of the source, each application configuration is uniformly initialized, verified and accessed as Clojure data.
- Verification before execution -
- End-to-end sweep preventing errors affecting programme implementation.
- Scrutinize the entire configuration for every meticulous execution.
- In case a problem is encountered, the user receives a prompt. E.g. a user has defined a file from which he/she will be reading some data, but has forgotten to create it. If the user specifies the file in the configurations, Omniconf will fail in the verification step even before the code is executed.
- Define in a single place, populate from many -
For verification, Omniconf provides the Verify API and hence we at FORMCEPT prefer to use Omniconf as our primary configuration library.
Code
Here is an example of using Omniconf with a very basic setup.
- Step 1: Require the omniconf core namespace in your demo namespace
[code language="clojure"]
(ns omniconf-demo.core
(:require [omniconf.core :as cfg])
(:gen-class))
[/code]
- Step 2: Define the syntax of the configuration your application expects
[code language="clojure"]
(cfg/define
{:hostname {:description "Where service is deployed"
:type :string
:required true}
:port {:description "HTTP port"
:type :number
:default 8080}
:conf {:description "Configuration file"
:type :file
:verifier omniconf.core/verify-file-exists}
:password {:description "Password for logging in"
:type :string}})
[/code]
- Step 3: Set the values of your parameters
Let’s add the port number as an environment variable. Open your bashrc file and add a line at the bottom-
[code language="bash"]
export PORT=8888
[/code]
Now, let’s set the value of the password in the java properties. To set this, modify your project.clj file to have this line in your dev profile:
[code language="clojure"]
jvm-opts ["-Dpassword=secret"]
[/code]
As can be seen in the syntax above, we have defined a :conf parameter of the type file. So, we’ll create a file named conf.edn and pass the :conf parameter to Omniconf at run time giving the value as the file name. The content of the file conf.edn should be a Clojure map. The current contents of the conf.edn file are:
[code language="clojure"]
{:hostname "formceptfile"}
[/code]
- Step 4: Run the program
In your main namespace, edit the main function to:
[code language="clojure"]
(defn -main
"Omniconf Demo"
[& args]
(cfg/populate-from-cmd args)
(cfg/populate-from-properties)
(when-let [conf (cfg/get :conf)]
(cfg/populate-from-file conf))
(cfg/populate-from-env)
(cfg/verify :quit-on-error true))
[/code]
And run the program
[code language="bash"]
lein run --conf conf.edn
[/code]
The output produced by the run command is as follows:
[code language="clojure"]
Omniconf configuration:
{:conf #object [java 1="0x421bba99" 2="conf.edn" language=".io.File"][/java],
:hostname "formceptfile",
:password "secret",
:port 8888}
[/code]
As we can see, the values of different configurations have been populated from different places. The :conf parameter was provided as a command line argument, :hostname was defined in the conf file conf.edn, :port was set as an environment variable and the value of :password was given as java property.
It is also worth noting that the order in which configuration are set is totally up to the user. Omniconf assigns priority to the sources in the same order in which it reads them. So, if you want your command line arguments to overwrite any of your other sources, read the command line arguments at the end. There are many applications of seamless configuration in actual implementation. For example, optimized configuration of hardware for streamlining hardware-software interface to avoid wastage and overburden is an important use-case of Omniconf. Recently, Mayank Pahwa from our team presented at The Bangalore Clojure User Group on managing application configuration conveniently.
At FORMCEPT, we constantly strive to share with you the best of upcoming technology and functionality, and come up with enterprise-wide innovative data analytics solutions at scale for businesses across a vast range of sectors. To know more about what we do and how we can help you, please write to us at contactus@formcept.com.