Frederik Heick is working for Miracle A/S a Danish based company, consulting, development and hosting of Oracle and JBoss applications. Frederik has posted 1 posts at DZone. You can read more from them at their website. View Full User Profile

Typed Properties in Java

12.03.2013
| 6253 views |
  • submit to reddit

Motivation

In typical java applications, especially web applications, you will have some properties.
Often you will have several properties files for different environments.

There is often a lot of boiler plate code to go with this. 

Example:

//Often with problems with paths etc.
Properties properties = ClassLoader.getResource("app.properties");
int myint = 0;
try {
	myint = Integer.parseInt(properties.getProperty("myint"));
	if ((myint<22) && (myint>658)) {
		//should I use default value?
		//should I throw exception ?
		//throw new IllegalArgumentException("myint property is outside valid range [23-657]");
	}
} catch (NumberFormatException e) {
	//Should we use default?
	//Should we just log the error?
	//Should we close application because of invalid property?
} catch (IllegalArgumentException e) {
	//Should we use default?
	//Should we just log the error?	
	//Should we close application because of invalid property?
}

Goals

We want to :
  • Have a clear one-line Property definition.
  • Load a Property as the type it was intended.
  • Have a simple way to initialize a collection of properties.

Solution

Using JHPropertiesTyped (Link) is defined by 3 steps:
  • Define your properties, with name, type, restrictions, description - typical in an interface.
  • Define your collection of properties using your already defined properties.
  • Define your PropertyIO.
    • This can include a PropertyValidationHandler, for invalid properties.
    • This can include a pre and post setup.
The definition would look like:
public interface MyPropertiesDef {
	public static final Property<Integer> MYINT = new Property<Integer>("myint",new IntegerPropertyType(23,657));
	public static final Property<Locale> MYLOCALE = new Property<Locale>("mylocale", new LocalePropertyType());
	public static final Property<MyColors> MYCOLORS = new Property<MyColors>("mycolor",new EnumPropertyType(MyColors.class));
}

The class would be like:

public class MyProperties extends PropertyStaticCollection implements MyPropertiesDef {

	private static MyProperties instance=null;
	
	public MyProperties() {
		//Per default a collection has a "new LoggingPropertyValidationHandler(false)" instance.
		super(new ResourceFilePropertyIO("app.properties"),new LoggingPropertyValidationHandler(true));
	}
	
	public MyProperties getInstance() {
		//Call this method at startup to initialize the properties
		if (instance==null) {
			instance = new MyProperties();
		}
		return instance;
	}
	@Override
	public void preValidation() {
	}
	@Override
	public void postValidation() throws PropertyException {
	}	
}

The "PropertyValidationHandler" performs an action when one or more properties are invalid. This can be logging a message, sending a message, stopping application. It is easy to implement your own PropertyValidationHandler.

In a collection there are two methods you can choose to override. 

  • preValidation: Usually to set global parameters if needed.
  • postValidation: Usually to check Property dependencies, using the ActivationValidator class. Example: That some properties must be well defined if a certain property is well defined.
A Property is defined as:
  • Property<Type> NAME = Property<Type>(String name,PropertyType<Type> type,boolean nullAllowed,boolean readonly,String description);
    • nullAllowed: is optional; default is false.
    • readonly: is optional; default is false.
    • description: is optional; default is null.

Accessing Data

Once the collection is initialized, you can access the values. You know they are valid, otherwise the PropertyValidationHandler should have taken care of it. Either way, if there is a property which is not valid, null is returned, so you will soon know. But there is a quick way to set up a unit test that will test all your different environment properties. (Read more below) 
Integer v1 = MyPropertiesDef.MYINT.getTypedValue();
Locale v2 = MyPropertiesDef.MYLOCALE.getTypedValue();
MyColors v3 = MyPropertiesDef.MYCOLORS.getTypedValue();

You can also set a value both using the typed value or the string equivalent. A value will NOT be set if the PropertyType deems it invalid. 

MyPropertiesDef.MYINT.setTypedValue(437);
MyPropertiesDef.MYINT.setValue("437");

You can access the methods on the collection. 

//For reloading
MyProperties.getInstance().getIo().reload();

//For getting backup
MyProperties.getInstance().getIo().backup();

//Getting property validation list
List<PropertyValidationDTO> list = MyProperties.getInstance().validation();

//Last saved
Date lastSaved = MyProperties.getInstance().getIo().getLastSaved();

Types Available

There are currently 39 distinct PropertyTypes : 

  • BooleanPropertyType
  • ByteSizePropertyType
  • CharsetPropertyType
  • ClassnamePropertyType
  • ColorPropertyType
  • DateFormatPropertyType
  • DateTimePropertyType
  • DecimalFormatPropertyType
  • DirectoryPropertyType
  • DoublePropertyType
  • EmailPropertyType
  • EnumPropertyType
  • EnvironmentPropertyType
  • FilenameFilterPropertyType
  • FilePropertyType
  • FileStructurePropertyType
  • HourIntervalsPropertyType
  • InetAddressPropertyType
  • IntegerPropertyType
  • IP4AddressPropertyType
  • LdapNamePropertyType
  • ListKeyValuePropertyType
  • ListPropertyType
  • LocalePropertyType
  • LongPropertyType
  • MonthPropertyType
  • PasswordPropertyType - Currently with AES, MD5 or SHA512 methods.
  • RegularExpressionPropertyType
  • SequencePropertyType
  • StringPropertiesPropertyType
  • StringPropertyType
  • TimeUnitPropertyType
  • TimeZonePropertyType
  • URIPropertyType
  • URLPropertyType
  • VersionPropertyType
  • WeekdayPropertyType

There are currently 16 distinct PropertyIO: 

  • AbstractPropertyIO
  • AbstractJDBCPropertyIO
  • AbstractXMLFilePropertyIO
  • AbstractXMLInputStreamPropertyIO
  • DatasourceJDBCPropertyIO
  • FilePropertyIO
  • InputStreamPropertyIO
  • JBossSystemPropertiesPropertyIO
  • MemoryPropertyIO
  • ResourceFilePropertyIO
  • ServletContextPropertyIO
  • UrlJDBCPropertyIO
  • WebContextParamsPropertyIO
  • CompositePropertyIO

It is easy to make your own PropertyType and PropertyIO.
It is easy to test all your different environment properties. (Read more below)

Unit Testing

Likely you have one or more properties for different environments. To test them all:

public class EnvironmentPropertyDataTest {	
	@Test
	public void testAllEnvironmentProperties() {
	
		PropertyFrameworkGlobals.setAutoEncryptPasswords(false);
		PropertyFrameworkGlobals.setValidateIPathPropertyTypes(false);
		
		File[] files = FilePropertyIOFactory.findPropertyFiles(new File("/config/environments"),"myapp.properties");		
		for (File file : files) {			
			MockTestCollection collection = new MockTestCollection(file);
			try {
				collection.validate();
			} catch (PropertyException e) {				
				fail("Property file ["+file.getAbsolutePath()+"] failed with message:"+e.getMessage());				
			}			
		}			
	}
	
	@After
	public void after() {
		PropertyFrameworkGlobals.resetGlobalSettings();
	}
	
	class MockTestCollection extends PropertyStaticCollection implements MyPropertiesDef {
		public MockTestCollection(File file) {
			super(new FilePropertyIO(file));
		}		
	}
}

Generation of Definitions

You can easily generate definitions for your existing properties. There are two ways: 

  • Using the main class in PropertiesJavaDefinitionPrinter.
  • Deploy "JHPropertiesTypedWebDemo" and go to the generate page.
Example of generation where input is:
my.email=email@email.com
my.int=123445
my.color=RED
my.list=PI;E;COS;SIN
Resulting in this:
//Value=[RED] - Suggesting type Color.
public final static Property<Color> MY_COLOR = new Property<Color>("my.color",new ColorPropertyType());

//Value=[email@email.com] - Suggesting type String.
public final static Property<String> MY_EMAIL = new Property<String>("my.email",new EmailPropertyType());

//Value=[123445] - Suggesting type Integer.
public final static Property<Integer> MY_INT = new Property<Integer>("my.int",new IntegerPropertyType());

//Value=[PI;E;COS;SIN] - Suggesting type List of String.
public final static Property<List<String>> MY_LIST = new Property<List<String>>("my.list",new ListPropertyType<String>(new StringPropertyType(),";"));

Libraries

There are 3 libraries I have released currently in version 1.5:

  • JHPropertiesTyped-<version>.jar
    • The main framework at around 200 kb size.
    • The only dependency is Java JDK 1.6, Apache Commons Logging, and Java Servlet API (If you use classes dependent of this API)
  • JHPropertiesTypedWeb-<version>.jar
    • Renderers for all ProepertyTypes, an abstract version.
    • Dependencies is Java JDK 1.6, Apache Commons Logging, and Java Servlet API 
  • JHPropertiesTypedWebDemo-<version>.war
    • A demo of all PropertyTypes, read-only and writeable, generation page.
    • Dependencies is Java JDK 1.6, Apache Commons Logging, and Java Servlet API 
There is a fourth library in the pipeline, where there are no dependency restrictions. But this is not yet released; it depends on user feedback.

Web Enabling

The library JHPropertiesTypedWeb-<version>.jar is a renderer implementation of all the PropertyTypes, both for a read-only and a writeable implementation. It is very easy to integrate this in your own web app. Your only need to write 3 style sheet tags, and provide two pictures.

Final Comments and Links

The framework has complete javadoc and extensive documentation. Currently the test coverage is at around 75%. You can find the framework here:

Full Disclosure

I'm the developer of JHPropertiesTyped. I'm writing this article because I'm proud of my work and I think that many others will find this useful. The software is open sourced and is free under LGPLv3 License. 

Published at DZone with permission of its author, Frederik Heick. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Frederik Heick replied on Tue, 2013/12/03 - 2:54pm

I would like some feedback, if any of you readers would spare the time for a short comment.

Fausto Almeida replied on Wed, 2013/12/04 - 10:59am

It looks very interesting. By the way, I would like to share an article I wrote some time ago if someone is looking for an less powerful but a little bit easier alternative to handle a properties file.

http://coudmonqui.blogspot.com/2013/04/api-para-el-manejo-de-archivos-de.html

Frederik Heick replied on Wed, 2013/12/04 - 1:27pm

I have been asked what I had in pipeline for future development.

For the main library I have

For the extension library

  • JHPropertiesTypedExtensions.<version>.jar
    • QuartzCronTriggerPropertyType (depends on Quartz library)
    • EmailValidationMessenger (depend on javamail)


Frederik Heick replied on Wed, 2013/12/04 - 3:44pm in response to: Fausto Almeida

That looks very interesting, I look have a closer view at it in few days.
My initial thoughts is

  • I haven't even considered using annotations, good idea. Could use it but difficult setting up restrictions on types.
  • Cool web design, simple but powerful.

Raging Infernoz replied on Sun, 2013/12/15 - 9:25am

Fixing property criteria to a name is inflexible. I am erring to doing everything config by only typed static adapter methods and objects.  I plan to migrate away from Properties objects to Preferences objects, because I find that I use a lot of tree based configuration, and doing this with Properties is just ugly; so I wrote code to copy Properties to/from Preferences object branches, even load/save Properties lines direct, and created custom in-memory Preferences objects, including support for case-insensitive names via TreeMaps.  I can see why lots of config. has moved to custom XML e.g. I discovered that Log4J properties config was useless, so switched to Log4J XML config; anyhow I now use SLF4J, and logback and logback.xml.  I only support properties files now because it can be easier for deployment and support staff.

Victor Homyakov replied on Fri, 2014/01/03 - 10:09am

if((myint<22) && (myint>658))

is it possible?

Frederik Heick replied on Thu, 2014/01/09 - 4:57pm in response to: Victor Homyakov

If you want then X to between 22 AND 658 (inclusive) than YES

Property<Integer> A_PROPERTY = new Property<Integer>("name", new IntegerPropertyType(22,658));

.If you want to be outside 22 and 658, meaning, X is less than 22 OR is greater 658 for it to be valid than NO.

But it is really easy to make your own PropertyType.
Either extend IntegerPropertyType or make your own.


Frederik Heick replied on Thu, 2014/01/09 - 5:06pm in response to: Victor Homyakov

public class OutsideIntegerPropertyType extends IntegerPropertyType {
	
	public OutsideIntegerPropertyType(Integer minValue,Integer maxValue) {
		super(null,minValue,maxValue);
	}
	
	@Override
	public Integer validate(String name, String value) throws PropertyException {
		validateNullType(name, value);
		try {
			int v = Integer.parseInt(value.trim());
			if (v>getMinValue() && v<getMaxValue()) {
				throw new PropertyException("Property ["+name+"] is ["+v+"] is inside illegal range["+getMinValue()+"-"+getMaxValue()+"].");
			} else {
				return v;
			}
		} catch (NumberFormatException e) {
			throw new PropertyException("Property ["+name+"] is not a valid integer ["+value+"].",e);
		}
	}

}

Frederik Heick replied on Fri, 2014/01/10 - 11:45am

Version 1.6 is now available. Enjoy the new features.

Victor Homyakov replied on Mon, 2014/01/13 - 6:44am in response to: Frederik Heick

Read your answer:

X is less than 22 OR is greater 658 

read your code again:

if((myint<22) && (myint>658))

How could be an integer simultaneously lesser than 22 AND greater than 658?

Frederik Heick replied on Wed, 2014/01/15 - 6:02pm in response to: Victor Homyakov

That was a typo. It should have been

if((myint<22) || (myint>658))


Frederik Heick replied on Wed, 2014/08/06 - 7:05pm

Version 1.7 is now available.

https://sourceforge.net/projects/jhpropertiestyp/?source=navbar

http://jhpropertiestyp.sourceforge.net 

Frederik Heick replied on Tue, 2014/10/07 - 12:17pm

Version 1.8 is now available.

https://sourceforge.net/projects/jhpropertiestyp/ 

Please read release notes : http://jhpropertiestyp.sourceforge.net/releasenotes.html

Enjoy !!!

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.