RapidProps

About

RapidProps is a Java library that facilitates the retrieval of configuration parameters. The only thing you have to provide is the interface used for retrieving the properties. RapidProps will dynamically generate an implementation of this interface. This process can be customized via annotations.

 

Hello, world!

Let's start with the classic Hello World program.

test.properties
  recipient = world
Hello.java
  package org.example.hello;

import java.io.IOException;
import net.sourceforge.rapidprops.PropsBuilder;

public class Hello {
public static interface MyProps {
String getRecipient();
}

public static void main(String[] args) throws IOException {
MyProps props = new PropsBuilder().createFromFile(MyProps.class, "test.properties");
System.out.println("Hello, " + props.getRecipient() + "!");
}
}
Output
  Hello, world!

The methods declared in the interface for retrieving the properties must have no arguments. A wide variety of return types are supported. Click here to see the full list.

PropsBuilder offers the following three methods for generating an implementation of the interface:

  • public <T> T createFromFile(Class<T> propsInterface, String filePath) throws IOException
  • public <T> T createFromResource(Class<T> propsInterface, String resourceName) throws IOException
  • public <T> T createFromMap(Class<T> propsInterface, Map<?,?> map)

The last method is typically called with an instance of java.util.Properties as the second argument. On error (other than I/O), the methods throw a runtime exception of type RapidPropsException.

By default, the property associated with a getter method is identified using the JavaBeans naming conventions. Alternatively, the desired property can be indicated by specifying an explicit key through the use of annotations, as shown in the following example.

 

A more complex example

This example illustrates the use of arrays, lists and maps as return types. It also shows the use of annotations in order to specify property keys, default values or non-standard delimiters for the elements of an array or list.

test.properties
  user.name = jsmith
failure.count.24h = 3;1;2;;;;;1;;;2;;4;;;;1;1;;2;2;3;;1
server.open.ports.192.168.1.101 = 22, 1521, 8080
server.open.ports.192.168.1.102 = 22, 53, 135
server.open.ports.192.168.1.103 = 80, 3690
MyProps.java
  package org.example.test;

import java.util.List;
import java.util.Map;

import net.sourceforge.rapidprops.Prop;

public interface MyProps {
@Prop(key="user.name", defaultValue="guest")
String userName();

@Prop(defaultValue="30000")
long getTimeout();

@Prop(key="failure.count.24h", delimiter=";", defaultElementValue="0")
List<Integer> getFailureCountLast24Hours();

@Prop(key="server.open.ports")
Map<String, Integer[]> getServerOpenPorts();
}
Test.java
  package org.example.test;

import java.io.IOException;
import java.util.Arrays;

import net.sourceforge.rapidprops.PropsBuilder;

public class Test {
public static void main(String[] args) throws IOException {
MyProps props = new PropsBuilder().createFromFile(MyProps.class, "test.properties");
System.out.println(props.userName() + ": timeout = " + props.getTimeout());
System.out.println("Failures last 24h: " + props.getFailureCountLast24Hours());
String myIP = "192.168.1.102";
System.out.println("Open ports for " + myIP + ": " + Arrays.asList(props.getServerOpenPorts().get(myIP)));
}
}
Output
  jsmith: timeout = 30000
Failures last 24h: [3, 1, 2, 0, 0, 0, 0, 1, 0, 0, 2, 0, 4, 0, 0, 0, 1, 1, 0, 2, 2, 3, 0, 1]
Open ports for 192.168.1.102: [22, 53, 135]

If a property with the required key does not exist and no defaultValue has been provided, a RapidPropsException is thrown.

By default, the elements of an array or list are comma separated. As shown in the case of failure.count.24h, a different delimiter may be specified. Additionally, a default value can be provided for the missing elements.

In the case of maps, the property key is used as the prefix of the actual map keys. In our example, the map keys are IP addresses (e.g., 192.168.1.102), which are appended using an additional dot to the property key (i.e., server.open.ports).

See props-builder.properties and PropsBuilderTest.java for more usage examples.

See date.properties and DateTest.java for usage examples involving java.util.Date.

See also ctor-enum.properties and ConstructorAndEnumTest.java for usage examples involving classes having a constructor with a single argument of type String (e.g., File, URL) and classes having a static method valueOf(String) or forName(String), which returns an instance of itself (e.g., enums or Class).

Null values

By default, RapidProps interprets the string "null" as the Java value null. This behavior can be changed by using the annotation property nullAlias, as shown in the example below.

test.properties
  names = John, , Mary, null, Bill, nobody, Kate
MyProps.java
  package org.example.test;

import net.sourceforge.rapidprops.Prop;
import net.sourceforge.rapidprops.PropConstants;

public interface MyProps {
String[] getNames();

@Prop(key="names", nullAlias="nobody")
String[] getNamesNobodyMeansNull();

@Prop(key="names", nullAlias="")
String[] getNamesEmptyMeansNull();

@Prop(key="names", nullAlias=PropConstants.DEFAULT_DELIMITER)
String[] getNamesNoNull();
}
Dynamically generated implementation
  package net.sourceforge.rapidprops;

import org.example.test.MyProps;

public final class MyPropsImpl$0 implements MyProps {
public final String[] getNames() {
return new String[] { "John", "", "Mary", null, "Bill", "nobody", "Kate" };
}

public final String[] getNamesNobodyMeansNull() {
return new String[] { "John", "", "Mary", "null", "Bill", null, "Kate" };
}

public final String[] getNamesEmptyMeansNull() {
return new String[] { "John", null, "Mary", "null", "Bill", "nobody", "Kate" };
}

public final String[] getNamesNoNull() {
return new String[] { "John", "", "Mary", "null", "Bill", "nobody", "Kate" };
}
}

We want that the method getNamesNoNull never interprets an element as the Java null value. Therefore we should use a nullAlias that is impossible to appear as element in the string array. The DEFAULT_DELIMITER is a good choice for this purpose.

 

Handlers

You can instruct RapidProps to transform the keys and/or values of the properties before dynamically generating the implementation. To this end, you add the Props annotation to the interface and configure a list of keyHandlers and/or a list of valueHandlers. You can also configure a list of valueHandlers within the Prop annotation. Aditionally, you can use the methods withKeyHandler(Handler keyHandler) and withValueHandler(Handler valueHandler) of PropsBuilder to configure key and value handlers. The example below illustrates the use of key and value handlers.

test.properties
  EMAIL.ADDRESSES = John.Smith@Example.Com, MARY.JOHNSON@EXAMPLE.COM, tOm.wiLLiAMs@eXaMPLe.coM
Test.java
  package org.example.test;

import java.io.IOException;
import java.util.List;

import net.sourceforge.rapidprops.Prop;
import net.sourceforge.rapidprops.Props;
import net.sourceforge.rapidprops.PropsBuilder;

public class Test {
@Props(keyHandlers=Props.ToLowerCase.class)
public static interface MyProps1 {
@Prop(key="email.addresses")
List<String> getEmailAddresses();
}

@Props(valueHandlers=Props.ToLowerCase.class)
public static interface MyProps2 {
@Prop(key="EMAIL.ADDRESSES")
List<String> getEmailAddresses();
}

@Props(keyHandlers=Props.ToCapitalizedFirstLetter.class, valueHandlers=Props.ToCapitalizedFirstLetter.class)
public static interface MyProps3 {
@Prop(key="Email.Addresses")
List<String> getEmailAddresses();
}

public static void main(String[] args) throws IOException {
MyProps1 props1 = new PropsBuilder().createFromFile(MyProps1.class, "test.properties");
System.out.println("Addresses #1: " + props1.getEmailAddresses());

MyProps2 props2 = new PropsBuilder().createFromFile(MyProps2.class, "test.properties");
System.out.println("Addresses #2: " + props2.getEmailAddresses());

MyProps3 props3 = new PropsBuilder().createFromFile(MyProps3.class, "test.properties");
System.out.println("Addresses #3: " + props3.getEmailAddresses());
}
}
Output
  Addresses #1: [John.Smith@Example.Com, MARY.JOHNSON@EXAMPLE.COM, tOm.wiLLiAMs@eXaMPLe.coM]
Addresses #2: [john.smith@example.com, mary.johnson@example.com, tom.williams@example.com]
Addresses #3: [John.Smith@Example.Com, Mary.Johnson@Example.Com, Tom.Williams@Example.Com]

See contacts.properties and HandlerTest.java for more usage examples using the predefined handler implementations ToLowerCase, ToUpperCase and ToCapitalizedFirstLetter.

RapidProps also offers the IncludeHandler, which allows including data from external resources using the #include directive. RapidProps uses by default this handler, therefore you don't need to configure it via annotations. For usage examples see include-test.properties, low-salary.sql, high-salary.sql and IncludeHandlerTest.java.

You can write your own handlers by implementing the net.sourceforge.rapidprops.Props.Handler interface:

  public interface Handler {
String handle(String data, Map<String, String> keyValues);
}

 

KeyValues

You can pass parameters to key and value handlers by configuring key value pairs. Both the Prop and the Props annotations allow defining lists of such pairs through the attribute keyValues.

Additionally, you can configure key values using the method withKeyValue(String key, String value) of PropsBuilder.

KeyValues are also used to configure the format of dates (see date.properties and DateTest.java) or to pass parameters to ValueBuilders (see valuebuilder.properties and ValueBuilderTest.java).

 

ValueBuilders

If your interface declares methods with return types that are not natively supported by RapidProps, you must configure value builders for these types. Each value builder must implement the ValueBuilder interface:

  public interface ValueBuilder<T> {
T createFromString(String sValue, Map<String, String> keyValues) throws Exception;
}

You can configure a list of value builders using the attribute valueBuilders of the Props annotation or a value builder for a given method using the attribute valueBuilder of its Prop annotation.

Additionally, you can configure value builders using the method withValueBuilder(ValueBuilder builder) of PropsBuilder.

The example below illustrates the use of value builders.

test.properties
  currencies = de, us, ru
CurrencyBuilder.java
  package org.example.test;

import java.util.Currency;
import java.util.Locale;
import java.util.Map;

import net.sourceforge.rapidprops.valuebuilder.ValueBuilder;

public class CurrencyBuilder implements ValueBuilder<Currency>{
@Override
public Currency createFromString(String sValue, Map<String, String> keyValues) throws Exception {
return Currency.getInstance(new Locale(sValue, sValue));
}
}
MyProps.java
  package org.example.test;

import java.util.Currency;
import java.util.List;

import net.sourceforge.rapidprops.Prop;

public interface MyProps {
@Prop(valueBuilder=CurrencyBuilder.class)
List<Currency> getCurrencies();
}
Test.java
  package org.example.test;

import java.io.IOException;

import net.sourceforge.rapidprops.PropsBuilder;

public class Test {
public static void main(String[] args) throws IOException {
MyProps props = new PropsBuilder().createFromFile(MyProps.class, "test.properties");
System.out.println("Currencies: " + props.getCurrencies());
}
}
Output
  Currencies: [EUR, USD, RUB]

See valuebuilder.properties and ValueBuilderTest.java for more usage examples.

RapidProps is available in the Maven central repository.

Maven
    <dependency>
<groupId>net.sourceforge.rapidprops</groupId>
<artifactId>rapidprops</artifactId>
<version>2.0.java8</version>
</dependency>
Ivy
    <dependency org="net.sourceforge.rapidprops" name="rapidprops" rev="2.0.java8"/>
Gradle
    'net.sourceforge.rapidprops:rapidprops:2.0.java8'

As you probably guessed, version 2.0.java8 requires Java 8. In order to use RapidProps with Java 5, 6, or 7, you need to configure version 2.0.java5 in your dependencies.

Here is an example of using RapidProps with Spring.

context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="builder" class="net.sourceforge.rapidprops.PropsBuilder"/>

<bean id="props" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject">
<ref local="builder"/>
</property>
<property name="targetMethod">
<value>createFromFile</value>
</property>
<property name="arguments">
<list>
<value>org.example.spring.MyProps</value>
<value>test.properties</value>
</list>
</property>
</bean>
</beans>
test.properties
  user = jsmith
timeout = 30000
openPorts = 22, 1521, 8080
MyProps.java
  package org.example.spring;

import java.util.List;

public interface MyProps {
String getUser();
long getTimeout();
List<Integer> getOpenPorts();
}
SpringDemo.java
  package org.example.spring;

import org.springframework.context.support.FileSystemXmlApplicationContext;

public class SpringDemo {
public static void main(String[] args) {
FileSystemXmlApplicationContext appContext = new FileSystemXmlApplicationContext("context.xml");
MyProps props = (MyProps)appContext.getBean("props");
System.out.println(props.getUser() +
": timeout = " + props.getTimeout() + ", openPorts = " + props.getOpenPorts());
}
}
Output
  jsmith: timeout = 30000, openPorts = [22, 1521, 8080]