I have some code that uses a class holding a huge lot of hardcoded constans. This is what it looks like:
class Constants{
public static final String name1 = "value1";
public static final String name2 = "value2";
public static final Integer value3 = 3;
... and so on
}
These constants are used everywhere in the code like Constants.name1
.
What I need to do now is to make it possible to specify values for these constants in a configuration file, probably a *.properties
file.
My question is: what is the best way to do it, having to rewrite as little code as possible?
I thought of using a single configuration class which reads properties from file when instantiated, but then I'll have to replace all the static calls of values to calls to an instance of that class and I'll have to alter existing methods to pass this config instance into them. Is there a better way?
Create a properties fileRight-click and select Add New Properties File. A new properties file will be added to your project. The new file will be selected and highlighted in the list. Type a name for your properties file, for example, "Properties".
The properties files are processed in the order in which they appear on the command line. Each properties file can refer to properties that have already been defined by a previously processed properties file, using ${varname} syntax.
Here is a piece of a code I have used in the past - that can be adapted to your example:
public enum Configuration {
PROPERTY1("property1.name", "default_value_1"),
PROPERTY2("property2.name", "default_value_2");
private final String key;
private String defaultValue;
Configuration(String key) {
this(key, NA);
}
Configuration(String key, String defaultValue) {
this.key = key;
this.defaultValue = defaultValue;
}
private final static Logger logger = LoggerFactory.getLogger(Configuration.class);
private final static String NA = "n.a.";
private final static String CONFIG_FILE = "properties/config.properties";
private final static String NOT_A_VALID_KEY = "Not a valid property key";
private final static Map<Configuration, String> configuration = new EnumMap<>(Configuration.class);
static {
readConfigurationFrom(CONFIG_FILE);
}
private static void readConfigurationFrom(String fileName) {
logger.info("Reading resource: {}", fileName);
try (InputStream resource = Configuration.class.getClassLoader().getResourceAsStream(fileName);) {
Properties properties = new Properties();
properties.load(resource); //throws a NPE if resource not founds
for (String key : properties.stringPropertyNames()) {
configuration.put(getConfigurationKey(key), properties.getProperty(key));
}
} catch (IllegalArgumentException | IOException | NullPointerException e) {
logger.error("Error while reading the properties file {}", fileName, e);
populateDefaultValues();
}
}
private static Configuration getConfigurationKey(String key) {
for (Configuration c : values()) {
if (c.key.equals(key)) {
return c;
}
}
throw new IllegalArgumentException(NOT_A_VALID_KEY + ": " + key);
}
private static void populateDefaultValues() {
for (Configuration c : values()) {
configuration.put(c, c.defaultValue);
}
}
/**
* @return the property corresponding to the key or null if not found
*/
public String get() {
return configuration.get(this);
}
}
Load the properties from the file using Properties.load(...)
and assign the constants from those properties.
class Constants{
public static final String name1;
public static final String name2;
public static final Integer value3;
static{
Properties p = new Properties();
try ( FileInputStream stream = new FileInputStream( new File("path/to/file.properties") )) {
p.load( stream );
}catch( Exception e ){
//handle exceptions
}
name1 = p.getProperty( "name1" );
name2 = p.getProperty( "name2" );
value3 = Integer.valueOf( p.getProperty( "value3" ) );
}
Note that this is just a quick and dirty solution and makes a lot of assumptions. It would be better to handle the individual exceptions and you'd also have to handle the NumberFormatException
that might be thrown by Integer.valueOf(...)
if the configuration is either empty or not a number.
Another note: you might also try and use some non-static or at least non-final configuration in order to be able to change properties at runtime.
Edit: I added autoclose for the the stream, but note that prior to Java 7 you'd have to handle that yourself.
As a quick hack, you can read the properties file in the static
initializer of the class, then you don't have to change the static nature of the class fields immediately, but I suggest you do that anyway over time.
Create a new class that holds all of your old constant values. From now on, inject this configuration object into your new code.
class NewConstants {
public final String name1;
public final String name2;
public final Integer value3;
... and so on
public NewConstants ( Properties props )
{
name1 = props.getProperty( "name1" );
...
}
}
Now refactor your old Constants
class
class Constants (
public static final String name1;
public static final String name2;
public static final Integer value3;
static {
Properties props = new Poperties( );
props.load( ... );
NewConstants newConstants = new NewConstants( props );
name1 = newConstants.getName1( );
name2 = newConstants.getName2( );
...
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With