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.
Let's start with the classic Hello World
program.
recipient = world
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() + "!");
}
}
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.
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.
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
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();
}
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)));
}
}
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).
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.
names = John, , Mary, null, Bill, nobody, Kate
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();
}
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.
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.
EMAIL.ADDRESSES = John.Smith@Example.Com, MARY.JOHNSON@EXAMPLE.COM, tOm.wiLLiAMs@eXaMPLe.coM
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());
}
}
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);
}
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).
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.
currencies = de, us, ru
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));
}
}
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();
}
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());
}
}
Currencies: [EUR, USD, RUB]
See valuebuilder.properties and ValueBuilderTest.java for more usage examples.
RapidProps is available in the Maven central repository.
<dependency>
<groupId>net.sourceforge.rapidprops</groupId>
<artifactId>rapidprops</artifactId>
<version>2.0.java8</version>
</dependency>
<dependency org="net.sourceforge.rapidprops" name="rapidprops" rev="2.0.java8"/>
'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.
<?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>
user = jsmith
timeout = 30000
openPorts = 22, 1521, 8080
package org.example.spring;
import java.util.List;
public interface MyProps {
String getUser();
long getTimeout();
List<Integer> getOpenPorts();
}
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());
}
}
jsmith: timeout = 30000, openPorts = [22, 1521, 8080]