Developer Tales or everything about everything

24Сен/130

Reading configuration files in Java. nProperty.

nproperty-logo

Many developers using configuration files (*.ini, *.prop, *.conf, etc.) in their applications. Java has standard class Properties, that provides ability to load and parse any configuration file. When amount of configuration files is large, reading and writing configuration files becomes very tiresome and routine work: create Properties object, convert each configuration to necessary format and write object field.

nProperty (Annotated Property) library is designed to simplify this process, halving configuration loader code.

Below are two examples to show how loaders code becomes lesser with using of nProperty. First example using standard Properties loader, second uses nProperty.

Both examples uses same configuration file:

Example #1:

Example #2:

Perhaps, these nice examples shows that code can be reduced more than twice in ideal circumstances :) These examples does not take a look around non-property fields and few another nuances. But let's go through it point by point.

Reading primitive and standard types

In second example above you can seen @Cfg annotation. This is the reason of code reducing. nProperty library based on annotations, which can be applied to classes, fields and methods.

To read properties that are primitive types it is enough to mark each class field by @Cfg annotation:

nProperty supports rich set of primitive and standard types:

  • Integer/int;
  • Short/short;
  • Double/double;
  • Long/long;
  • Boolean/boolean;
  • String;
  • Character/char;
  • Byte/byte;
  • AtomicInteger, AtomicLong, AtomicBoolean;
  • BigInteger, BigDecimal.

All this types can be used in example above.

Deserialization into arrays and collections

Beside standard types reading into arrays is possible. There is one important condition - array type should be one of standard types:

In this case library will take a sense about array initialization.

Take a look on SOME_INT_ARRAY and SOME_SHORT_ARRAY field annotations. By default nProperty uses ";" symbol as string separator. But it can be easily re-defined by setting splitter annotation property. And separator can be fully-qualified regular expression.

Beside arrays deserialization into collection also possible, namely - into lists. There is one important condition - collection should be initiated before parsing started. This is because different implementations of collections may be used (ArrayList, LinkedList, etc.):

Otherwise collections follows all rules of arrays.

Deserialization into user types

As additional function, library can work with custom user classes. Existing constructor MyClass(String) is primarily required. In other cases exception will be thrown. Constructor visibility level does not matters, he can be public, protected, or private:

As you see visibility level of constructor in example above is private. Configuration file property value will be written to field "value" of class T as result of this example.

It is obvious that standard Java class can be used as user-type classes. For example, it is possible to initialize java.io.File objects by loading file paths from configuration file:

So, in this example MY_FILE_PATH variable will refer to object initialized with File(String) ctor.

Access level modifiers

For nProperty library it is not matters what are access level modifiers of field, method or constructor - library works with Reflections mechanism and manages visibility by its own. Of course, this injection will not affect another application parts, with which library does not work.

All members initialization

In past examples each field was annotated with @Cfg annotation. So, that necesity can produce very much @Cfg annotations in file. To avoid routine work nProperty allows annotate whole class instead of each field. When annotating class with @Cfg annotation, all fields of class becomes potential property fields:

Take a look at log field from example above. This field annotated by @Cfg annotation with ignore property enabled. This property means that annotated field will not be used by library during configuration reading, and just will be skipped. This property should be used only when annotating whole class, as in example above.

Default values

The grace feature of the library is that if property was not met in configuration file, field value will be never changed by nProperty. This allows to use default values right in field declaration:

In this case WRONG_PROPERTY will contain 9000 value even after parsing configuration file.

Names overriding

In cases when field name does not equals configuration property name, its name can be overrided:

Of course, if it is possible to save equivalence between field names and property names, then it is better choice - this allows avoid annotating each field.

Working with non-static members

Library can work with classes and with class instances. This can be defined by different calls of ConfigParser.parse() method:

As you can see in example above, two different calls of same method was used. After ConfigParser.parse(Example11.class, "config/example.ini") line SOME_INT_VALUE will be zero, and this fact does not depends on configuration file - this field is not static and cannot be used without class instance.

After second call ConfigParser.parse(new Example11(), "config/example.ini") SOME_INT_VALUE will be filled with value from configuration file.

This feature should be used with accuracy. You can loose configuration value when using non-static field in static context.

Using of class methods

Let assume that during reading some property from file, its value should be checked or, for example, deserialized in unusual way. That task can be done by three ways:

  1. check value after library will analyze file;
  2. create custom user type as value wrapper with constructor (as was shown above);
  3. exclude class field and redirect property to method.

Most easy and correct way is #3. nProperty library allows to work not only with field, but with methods:

Here's checkIntArray(String) method and property value SOME_INT_ARRAY from configuration file will be passed to its first parameter. It is very convenient way when standard library solutions are out. You may put any code you want in handler method.

However, it is important, that library does not uses splitter mechanism when working with methods, i.e. at now it is impossible to organize automatic array deserialization within methods.

But type conversions still supported if first method parameter is differs from String.

As with class fields, if methods name is equals to property name in configuration file, setting up property name is unnecessary.

Events handling

nProperty library allows to handle some useful events during reading configuration files. To use event handling class should implement IPropertyListener interface and all its abstract methods. Event handling is possible only when library works with class instances (real objects, not static classes). Supported events:

  • onStart(String path) - called before loading configuration file;
  • onPropertyMiss(String name) - called when some property was not found in configuration file, but was marked in annotated class with @Cfg;
  • onDone(String path) - called when configuration parsing ends;
  • onInvalidPropertyCast(String name, String value) - called in circumstances of property type casing failure, when property was successfully read from file, but was unable to cast to type specified in associated field.

In example above all four events will be called. Event onPropertyMiss will be called for SOME_MISSED_VALUE property, which is not in configuration file. Event onInvlidPropertyCast will be called for SOME_INT_ARRAY field which type is int.

Using streams and file descriptors

Library can receive not only file names as pointers to configuration resources. Also java.io.File and streams (inherited from java.io.InputStream) passing is allowed:

As you can see when working with streams the library required additionally to set up configuration name, because it is not possible to retrieve resource name from low-level FileInputStream object. Configuration name is not important part of call - it is used only for displaying information (including events).

So, data can be retrieved not only from file system, but from any data source that is working with Java standards. Ability to work with streams allows to use library on Android:

Additional Features

Using prefixes

Sometimes in configuration files prefixes are used as logical separators of configuration options. For example, like this:

nProperty provides ability to set up prefix value for each class or object field to avoid setting up prefixes per each property field:

Prefix parameter of @Cfg class annotation defines prefix for each field: user, pswd, host, port. To override prefix value, prefix parameter should be set for specified field.

Parametrization

In some circumstances configuration property parametrization is needed. For example, file system root path can be stored in separated property and then used in other properties to specify file full path.

Parameter is any notation: ${some_name}, where some_name - configuration file property name which value should be placed instead of parameter.

Notation ${} is not a parameter, however, it is special sequence of symbols which will be replaced with "$" sign.

Let's see at example of setting up SQL Link based on parameters we have:

And corresponding loader class:

There was "parameterize" parameter enabled for sqlink class field. Field db.sqlink will be: "mysql://user@127.0.0.1:90" and it will be written to class field sqlink.

It is important to note that before v1.3.1 replacing values are taken directly from configuration file. So, next notation:

will produce "${prop1}bar" value in property prop3. It means that nProperty had no support for circular parametrization. To get "foobar" value next changes should be applied in this example:

But from version 1.3.1 recursive parametrization support presented. So, from v1.3.1 prop3's propery value from previous example will be "foobar".

Configuration file generation

Ability to create configuration files based on Java-classes was added to nProperty. This ability can be used to automatically generate configuration files, changing configuration and store changes. Feature of nProperty storage is that nProperty does not sort property fields like java.util.Properties.

To store current class or class instance state to file, ConfigParser.store(...) method should be called. This method has few implementations:

All fields marked with @Cfg annotation will be putted to file. Property values will be same as current field values in class or object. Default values are also used.

XML support

From version 1.4 nProperty supports loading from and storing to XML files. Like the Java standard, XML files should meet DTD requirements:

Same DTD used in java.util.Properties class. Loading and storing properties from XML doesn't differs from loading or storing properties in ini-format. Absolutely same API added for working with XML:

XML API have just "...Xml(...)" postfix.

And as benefit, like in ini-files, nProperty saves key ordering when storing to XML files.

Notes

According to JVM limitations and non stability of algorithm technical implementation, the library cannot work with final fields.

License

Library is distributed under Apache License v2.0.

Links

Просмотров: 4373
Комментарии (0) Пинги (0)

Пока нет комментариев.


Leave a comment


− два = 6

http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_bye.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_good.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_negative.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_scratch.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_wacko.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_yahoo.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_cool.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_heart.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_rose.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_smile.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_whistle3.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_yes.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_cry.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_mail.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_sad.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_unsure.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_wink.gif 
 

Trackbacks are disabled.