Click here to Skip to main content
15,881,882 members
Articles / Programming Languages / Scala
Technical Blog

SCALA : CONFIG

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
17 Nov 2015CPOL4 min read 9.6K  
Configuration in Scala

So we progress with the series of posts for .NET devs that may want to try their luck with Scala. This time, we will be talking about configuration.

As I have stated quite a lot already, I am a seasoned .NET developer that is getting into Scala.

Those of you that have worked in the .NET space will know that you can use the CofigurationManager class to help you read App.Config, or Web.Config file. Where the XXX.Config files are stored as XML, a typical example being something like the ones shown below:

XML
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="log4net" 
    type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
  </configSections>
  <log4net>
    <appender name="ConsoleAppender" 
    type="log4net.Appender.ConsoleAppender">
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date 
        [%4.4thread] %-5level %20.20logger{1} - %message%newline" />
      </layout>
    </appender>
    <appender name="RollingLogFileAppender" 
    type="log4net.Appender.RollingFileAppender">
      <file value="Client.log" />
      <appendToFile value="true" />
      <rollingStyle value="Composite" />
      <datePattern value="yyyyMMdd" />
      <maxSizeRollBackups value="10" />
      <maximumFileSize value="1MB" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date [%4.4thread] 
        %-5level %20.20logger{1} - %message%newline" />
      </layout>
    </appender>
    <root>
      <level value="INFO" />
      <appender-ref ref="ConsoleAppender" />
      <appender-ref ref="RollingLogFileAppender" />
    </root>
  </log4net>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
  <runtime>
     <runtime>
      <loadFromRemoteSources enabled="true" />
   </runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Owin" 
        publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-3.0.0.0" 
        newVersion="3.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Owin.Security" 
        publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-3.0.0.0" 
        newVersion="3.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" 
        publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-7.0.0.0" 
        newVersion="7.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Cors" 
        publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-5.2.2.0" 
        newVersion="5.2.2.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

The .NET configuration system is also quite flexible in that it allows you to create custom sections, but this requires a lot of code.

Here is an example: https://msdn.microsoft.com/en-us/library/2tw134k3.aspx up on date 12/11/15

We want to add a custom section which contains 2 properties:

  • font
  • color

So we would have to add code similar to this to the actual XXXX.config file:

XML
<configuration>
<!-- Configuration section-handler declaration area. -->
  <configSections>
    <sectionGroup name="pageAppearanceGroup">
      <section
        name="pageAppearance"
        type="Samples.AspNet.PageAppearanceSection"
        allowLocation="true"
        allowDefinition="Everywhere"
      />
    </sectionGroup>
      <!-- Other <section> and <sectionGroup> elements. -->
  </configSections>
 
 <!-- Configuration section settings area. -->
  <pageAppearanceGroup>
    <pageAppearance remoteOnly="true">
      <font name="TimesNewRoman" size="18"/>
      <color background="000000" foreground="FFFFFF"/>
    </pageAppearance>
  </pageAppearanceGroup> 
 
</configuration>

And also create the following C# code to create this custom set of XML tags:

C#
using System;
using System.Collections;
using System.Text;
using System.Configuration;
using System.Xml;
 
namespace Samples.AspNet
{
    public class PageAppearanceSection : ConfigurationSection
    {
        // Create a "remoteOnly" attribute.
        [ConfigurationProperty("remoteOnly", 
        DefaultValue = "false", IsRequired = false)]
        public Boolean RemoteOnly
        {
            get
            { 
                return (Boolean)this["remoteOnly"]; 
            }
            set
            { 
                this["remoteOnly"] = value; 
            }
        }
 
        // Create a "font" element.
        [ConfigurationProperty("font")]
        public FontElement Font
        {
            get
            { 
                return (FontElement)this["font"]; }
            set
            { this["font"] = value; }
        }
 
        // Create a "color element."
        [ConfigurationProperty("color")]
        public ColorElement Color
        {
            get
            {
                return (ColorElement)this["color"];
            }
            set
            { this["color"] = value; }
        }
    }
 
    // Define the "font" element
    // with "name" and "size" attributes.
    public class FontElement : ConfigurationElement
    {
        [ConfigurationProperty("name", 
        DefaultValue="Arial", IsRequired = true)]
        [StringValidator(InvalidCharacters = 
        "~!@#$%^&*()[]{}/;'\"|\\", MinLength = 1, MaxLength = 60)]
        public String Name
        {
            get
            {
                return (String)this["name"];
            }
            set
            {
                this["name"] = value;
            }
        }
 
        [ConfigurationProperty("size", 
        DefaultValue = "12", IsRequired = false)]
        [IntegerValidator(ExcludeRange = false, MaxValue = 24, MinValue = 6)]
        public int Size
        {
            get
            { return (int)this["size"]; }
            set
            { this["size"] = value; }
        }
    }
 
    // Define the "color" element 
    // with "background" and "foreground" attributes.
    public class ColorElement : ConfigurationElement
    {
        [ConfigurationProperty("background", 
        DefaultValue = "FFFFFF", IsRequired = true)]
        [StringValidator(InvalidCharacters = 
        "~!@#$%^&*()[]{}/;'\"|\\GHIJKLMNOPQRSTUVWXYZ", MinLength = 6, MaxLength = 6)]
        public String Background
        {
            get
            {
                return (String)this["background"];
            }
            set
            {
                this["background"] = value;
            }
        }
 
        [ConfigurationProperty("foreground", 
        DefaultValue = "000000", IsRequired = true)]
        [StringValidator(InvalidCharacters = 
        "~!@#$%^&*()[]{}/;'\"|\\GHIJKLMNOPQRSTUVWXYZ", MinLength = 6, MaxLength = 6)]
        public String Foreground
        {
            get
            {
                return (String)this["foreground"];
            }
            set
            {
                this["foreground"] = value;
            }
        } 
    } 
}

Which we can then access from code like this:

C#
Samples.AspNet.PageAppearanceSection config =
        (Samples.AspNet.PageAppearanceSection)System.Configuration.ConfigurationManager.GetSection(
        "pageAppearanceGroup/pageAppearance");
var cfgFont = config.Font.Name

Phew, that’s a lot of work.

There are other ways to do this in C#. I am thinking of the awesome SimpleConfig GitHub repo, which in my opinion is well underrated and something that we should all use in our .NET projects.

https://github.com/mikeobrien/SimpleConfig up on date 12/11/15

Using this, we can now write code like this (instead of the above, which is a BIG improvement if you ask me).

First, create your configuration types:

C#
public class MyApplication
{
    public Build Build { get; set; }
}
 
public enum Target { Dev, CI }
 
public class Build
{
    public string Version { get; set; }
    public DateTime Date { get; set; }
    public Target DeployTarget { get; set; }
}

Next, you need to register the SimpleConfig section handler in your web/app.config and create your configuration section as shown below. The default convention for the section name is the camel cased name of the root configuration type (Although you can override this as we’ll see later). The section name under configSections must match the section element name. All other element and attribute names in the configuration section are case insensitive but must otherwise match the property names of your configuration types (You can override this as well).

XML
<configuration>
  <configSections>
    <section name="myApplication" 
    type="SimpleConfig.Section, SimpleConfig"/>
  </configSections>
  <myApplication>
    <build version="0.0.0.0" 
    date="10/25/1985" deployTarget="Dev"/>
  </myApplication>
</configuration>

Now you can load the section either by calling the convenience static method or newing up a new instance:

C#
ar config = Configuration.Load<MyApplication>();
// or
var config = new Configuration().LoadSection<MyApplication>();
 
config.Build.Date.ShouldEqual(DateTime.Parse("10/25/1985"));
config.Build.DeployTarget.ShouldEqual(Target.Dev);
config.Build.Version.ShouldEqual("0.0.0.0");

This is cool, however Scala does it even better. The rest of this post will be about the awesome Typesafe Config library (Typesafe are the people behind Akka (I like Akka)).

Typesafe Config Library

The guys from Typesafe have an awesome config library (https://github.com/typesafehub/config) that you may use with either Java/Scala, and it supports the following 3 formats:

  • JSON
  • Java properties
  • HOCON (Human-Optimized Config Object Notation)

For everything I demonstrate here, I will be using the following HOCON file.

C#
sachas.business {
  owner {
    name = "sacha barber"
    description = ${sachas.business.owner.name} "is the owner"
  }
  team {
    members = [
      "sacha barber"
      "chris baker"
      "paul freeman"
      "ryan the mad one"
    ]
  }
}
sachas.business.team.avgAge = 35

If you want to try this out in your own scala project, you will need to add it as a SBT library dependency, using this (the version shown here was right at the time of this post being published).

C#
libraryDependencies ++= Seq(
  "com.typesafe" % "config" % "0.4.0"
)

So what can this Typesafe library do?

Well it essentially reads configuration information from file(s). This would typically be done using a application.conf file, which would be placed in your resources folder.

image

After you have a file, we can proceed to load it using the ConfigFactory, which you can use like this:

C#
import com.typesafe.config.ConfigFactory 
 
object ClassesDemo {
 
  def main(args: Array[String]) : Unit =
  {
 
    val config = ConfigFactory.load("application.conf")
    .....
    .....
    .....
    System.in.read()
  }
}

Well, let’s start simple by using the HOCON file we outlined above:

C#
import com.typesafe.config.ConfigFactory
 
object ClassesDemo { 
 
  def main(args: Array[String]) : Unit =
  {
 
    val config = ConfigFactory.load("application.conf")
    val ownerName = config.getString
    ("sachas.business.owner.name")  // => sacha barber
    val desc = config.getString
    ("sachas.business.owner.description") // => sacha barber is the owner
    val age = config.getInt
    ("sachas.business.team.avgAge ") // => 35
    val members = config.getStringList
    ("sachas.business.team.members") // => [sacha barber,chris baker,paul freeman,an the mad one
 
    System.in.read()
  }
}

It can be seen that we can easily drill into the tree of properties, and use the getXXX methods to grab strings, list and all sorts of goodness.

The above code gives this result:

image

Pretty simple huh!

The Config object has these helper methods to enable you to read configuration values:

  • getAnyRef
  • getAnyRefList
  • getBoolean
  • getBooleanList
  • getByte
  • getByteList
  • getConfig
  • getConfigList
  • getDouble
  • getDoubleList
  • getInt
  • getIntList
  • getList
  • getLong
  • getLongList
  • getMilliSeconds
  • getMilliSecondsList
  • getNanoSeconds
  • getNanoSecondsList
  • getNumber
  • getNumberList
  • getObject
  • getObjectList
  • getString
  • getStringList
  • getValue

I think most of these are quite obvious, perhaps the only one that I personally feel may need a bit more of an explanation are the getObject/getObjectList methods. So let’s have a look at a specific example for this.

Say we have this HOCON file:

C#
ecoders = [ { a : "socket://1.2.3.4:9000" },
  { b : "socket://1.2.3.4:8080" },
  { c : "socket://9.9.9.9:9001" },
  { d : "socket://9.9.8.8:9000" },
  { e : "socket://4.3.2.1:8081" } ]

Which we then read in like this:

C#
import com.typesafe.config.{ConfigObject, ConfigValue, ConfigFactory, Config}
import scala.collection.JavaConverters._
import java.net.URI
import java.util.Map.Entry
 
case class Settings(config: Config) {
  lazy val decoders : Map[String, URI] = {
    val list : Iterable[ConfigObject] = config.getObjectList("decoders").asScala
    (for {
      item : ConfigObject <- list
      entry : Entry[String, ConfigValue] <- item.entrySet().asScala
      key = entry.getKey
      uri = new URI(entry.getValue.unwrapped().toString)
    } yield (key, uri)).toMap
  }
} 
 
object ClassesDemo {
 
  def main(args: Array[String]) : Unit =
  { 
    val config = ConfigFactory.load("smallerList.conf")
    val decoders = new Settings(config).decoders
 
    System.in.read()
  }
}

Which gives us the following results:

image

I have shamelessly stolen this example from this blog, which is a very nice example in my opinion:

Further Readings

I came across a couple of good blogs on this whilst writing my own post. These are outlined here:

This article was originally posted at https://sachabarbs.wordpress.com/2015/11/16/scala-config
This article is part of the series 'Scala View All

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
-- There are no messages in this forum --