1.2 - Configuration through properties

All parts of SDK are custimizable through configuration files. These are almost normal java's properties files, at least their structure is. These property files define various parameters and properties that are use by SDK to configure its behaviour.

The SDK provides all neccessary properties directly in the JAR file. They are called "builtin" properties. You will find them in the classpath in "resources" directory. There are currently two properties files:

  • builtin-config.properties - provide general SDK configuration
  • builtin-logger.properties - provide logging facility configuration

Both of these default builtin configuration can be easily overriden by placing a new configurarion file anywhere in the SDK's search path (described below.) The user supplied properties are combined with the builtin ones, overriding any properties that conflict with the user properties taking the precence.

As a quick example, if you place a file alled "config.properties" or "logger.properties" in you current working directory, when you start up jNetPcap SDK, that file will be found and loaded, overriding any builtin properties that were defined. You can also specify any properties you like on the command line when starting the java VM with the usual "-D" command line option. The precedence of properties is as follows:

  1. System properties - defined on the command line
  2. user properties - defined in a user properties file
  3. builtin properites - default properties supplied with the SDK.

Properties file pre-processor

There is a pre-processor applied to properties files, that recombines property lines that have been escaped with a backslash-newline at the end of a property line. This pre processor is applied directly on the IO stream when the file is being read by a normal java Properies class. This pre processor then allows properties to be defined on multiple consecutive lines escaped with backslash-newline escape sequence. Here is an example

property1 = value 1 \
  value 2 \
  value 3
propert2 = value 4

After the pre processor, the file would like like this:

property1 = value 1 value 2 value 3
property2 = value 4

Otherwise, they are completely normal Property files.

Expandable properties

Properties are retrieved from the properties file using normal Properties.getProperty and Properties.setProperty accessor methods. The values can be post processed by SDK to provide additional functionality. This is where ExpandableString and ConfigString come in. These are fancy strings that allow replacement of certain special variables and properties that are defined within them. They lookup the values of the properties and variables that are embeded in the string using supplied SDK's configuartion environment. For example:

property1 = hello
property2 = @{property1}

These 2 properties, when expanded, will both contain the value of "hello". Here is a code sample:

String property1 = JConfig.getProperty("property1");
String property2 = JConfig.getExpandedProperty("property2");

System.out.println("property1=%s and property2=%s\n", property1, property2);

// Output produced is 
// property1=hello and property2=hello

Properties can reference other properties that reference other properties. The expansion happens recursively until the string can no longer be expanded or until a reference to another property is invalid. Either way the expansion stops.

This is a powerful way for properties to inherit their values from other, possibly more global properties, while providing the flexibility of smaller configuration units.

Search Paths

The real power of expandable properties is brought to fruition in search paths. A search path, is a property that defines SearchPath expressions for various kinds of search spaces. There are currently 3 kinds of search types possible:

  • File - performs a search of the local filesystem
  • Classpath - perfroms a search within a CLASSPATH
  • URL - uses a URL to locate a resource

A search path is a normal property that defines a combination of these 3 types of search elements. You can use each element any number of times with a search path and you can reference other search path properties. All of their values will be combined. Here is an example of the main search path the SDK uses to search for resources such as config files:

search.path = \
    'File(@{user.home}/@{resources.subdir}/${name})' \
    'File(@{${name}})' \
    'File(@{user.dir}/${name})' \
    'File(@{java.io.tmpdir}/${name})' \
    'Classpath(resources/${name})' \
    'Classpath(${name})' \
    'URL(@{resources.${name}.url})'

Lets start by describing what is ${name}. Its a variable supplied at runtime and is usually the target of the search. So if we were seearching for "config.properties" file ${name} would expand to "config.properties". @{user.home} property is a standard system property inherited from System.getProperties() call. So is @{usr.dir} which is the current working directory.

The search path has 4 File search elements, 2 Classpath elements and 1 URL based search element. They are searched in order they appear in the declaration. Lets take a look at the first entry and substitute some concrete values. Assume @{user.home} = /home/guest, @{resources.subdir} = .jnp and we are looking for file named "oui.txt". The first line would expand to:

'File(/home/guest/.jnp/oui.txt)'

Very clean result for such a complex looking line. The next ones are even cleaner:

search.path = \
    'File(/home/guest/.jnp/oui.txt)' \
    'File(oui.txt)' \
    'File(./oui.txt)' \
    'File(/tmp/oui.txt)' \
    'Classpath(resources/oui.txt)' \
    'Classpath(oui.txt)' \
    'URL(@{resources.oui.txt.url})'

Notice that last entry @{resources.oui.txt.url}, was a little special. That is we had a reference to a variable ${} within the a property reference. Variables are always expanded first, then the properties are expanded on the result of the first variable expansion. In our case, we don't know what @{resources.oui.txt.url} property is, its not defined, therefore that expansion fails to resolve completely. It will be ignored. The remainder expanded just fine and appropriate search path algorithms are used to look for that resource. There are other search paths defined in the builtin config used to locate various resources, temp files, home direcotry for jNetPcap files, etc.

Its also worth noting that these search paths and any properties for that matter can inherit other property values, no matter how complex. Best example is the address resolvers. Address resolvers resolve a binary address to human label, such as ip address to hostname. The mapped result is cached in a cache file, which is stored on the local disk. Resolver's use properties to define numerous properties including a search path that they use to search for cache files. Instead of rewritting a search path from scratch, you can provide a reference to already standard search path like this:

resolver.search.path = @{search.path}

And the entire search path we just discussed, will be inherited by this new property. The user can override the search path in a user config file by simply defining a new value to property "resolver.search.path", or leave things at default using search path. If the user overrides the default "search.path" property any other properties that reference the global default search path will also be changed all at the same time.