How to use config properties file throughout the classes? - java

I need my Java app to read the config properties from a file and use them throughout the classes. I'm thinking of a separate class, that would return a map of property_key:property_value for each of the properties in the file. Then I would read the values from this map in other classes.
Maybe there are other, more commonly used options?
My properties file is simple and has about 15 entries.

Just use java.util.Properties to load it. It implements Map already.
You can load and get hold of the properties statically. Here's an example assuming that you've a config.properties file in the com.example package:
public final class Config {
private static final Properties properties = new Properties();
static {
try {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
properties.load(loader.getResourceAsStream("com/example/config.properties"));
} catch (IOException e) {
throw new ExceptionInInitializerError(e);
}
}
public static String getSetting(String key) {
return properties.getProperty(key);
}
// ...
}
Which can be used as
String foo = Config.getSetting("foo");
// ...
You could if necessary abstract this implementation away by an interface and get the instance by an abstract factory.

Related

Google Guice Properties Management

I would like to create a proper properties management strategy in a java webapp that relays on google guice as a DI framework.
I would like to have a mechanism answering the following 3 requirements:
I would like to be able to inject properties using guice (#Named)
I would like to be able to access properties in a static way
The mechanism should support prioritization of properties, meaning that a property can be wrapped in the deployed war with a certain value but it can also be redundant in the target system level or local file system (of the target machine I deploy on), in such a case the value in the war will be overridden by the value that exists in the target machine.
I believe this is a standard requirement. Now, using guice standard binder I can easily get the first requirement but not the other two. To get the other two I created my own class that does the following:
Wraps and exposes the binding methods of guice (those that binds properties) For example:
public static void bindString(AnnotatedBindingBuilder<String> binder, String property, String defaultValue) {
binder.annotatedWith(Names.named(property)).toInstance(getProperty(property, defaultValue));
}
Where the getProperty method knows how to handle my properties (get the value from the war or system level) and exposes the properties statically as well.
So basically as long as I'm using this utility that I created for properties bindings I'm good, it covers all my requirements but once I use the standard guice bindings I'm losing the second and third requirement.
Is there a way to override guice bindings and get all those 3 requirements?
Once I had the same challange in a spring based app and was pretty easy. I implemented ApplicationContextInitializer with the following method:
#Override
public void initialize(ConfigurableWebApplicationContext ctx) {
PropertySource<Map<String, Object>> localProps = null;
try {
localProps = new ResourcePropertySource(new ClassPathResource(LOCAL_PROPERTIES_FILE_NAME));
} catch (IOException e) {
LOG.fatal("Could not load local properties from classpath " + LOCAL_PROPERTIES_FILE_NAME);
return;
}
LOG.info("Loaded configuration from classpath local file " + LOCAL_PROPERTIES_FILE_NAME);
ctx.getEnvironment().getPropertySources().addFirst(localProps);
}
so this gave me a way to add local properties with highest priority to my Environment. In case of overlap with war properties the local ones had higher priority. In addition I exposed my Environment statically so I has static access to my properties (for services that are not managed by the container, legacy mostly).
How can I achieve this with guice?
Unfortunately, I don't think that you are going to find anything that gives you a truly clean and satisfying implementation. Especially, I don't think that you will find anything that gives you exactly what you want without implementing at least portions of it yourself.
If I had those needs, I would make sure that my injector is created in a central InjectorFactory. If you require a large number of parameters from outside to create your injector, I would simply create it once at the very beginning of my application and then cache the injector into a static final field. This would make it available to a static method. I would bind my "fall-back" property loading to an explicit provider. That way, instead of using the standard Names.bindProperties(...) method, I would bind it directly to a Provider. This provider then implements the logic that is necessary to perform the fallback or to merge multiple property files. Having the injector cached to a static field means that I can call a static method to access properties from a global-context outside of my injected classes.
Using your own provider seems initially unpleasant, but can provide some additional benefits. For starters, you can implement your fallback strategy exactly how you want. Additionally, you can add additional behaviors such as auto-reloading your property files, etc (not shown in my code sample).
public class InjectorFactory {
private static Injector injector = null;
public static synchronized Injector getOrCreateInjector() {
if(injector == null) {
injector = Guice.createInjector(new AbstractModule() {
#Override
protected void configure() {
Properties properties1 = createProperties("file1.properties");
Properties properties2 = createProperties("file2.properties");
Set<Object> propertyNames = new HashSet<Object>();
propertyNames.addAll(properties1.keySet());
propertyNames.addAll(properties2.keySet());
for (Object object : propertyNames) {
String propertyName = (String) object;
bind(String.class).annotatedWith(Names.named(propertyName)).toProvider(new StringProvider(properties1, properties2, propertyName));
}
}
private Properties createProperties(String propertyFileName) {
try {
InputStream stream = InjectorFactory.class.getResourceAsStream(propertyFileName);
try {
Properties properties = new Properties();
properties.load(stream);
return properties;
} finally {
stream.close();
}
} catch (IOException exception) {
throw new RuntimeException("Could not load properties file");
}
}
});
}
return injector;
}
public static String getProperty(String propertyName) {
return getOrCreateInjector().getInstance(Key.get(String.class, Names.named(propertyName)));
}
}
Given the above code and file1.properties:
property1=Property1Value
property2=Property2Value
And file.properties:
property2=IncorrectProperty2Value
property3=Property3Value
with the provider
public class StringProvider implements Provider<String> {
private Properties properties1;
private Properties properties2;
private String propertyName;
public StringProvider(Properties properties1, Properties properties2,
String propertyName) {
this.properties1 = properties1;
this.properties2 = properties2;
this.propertyName = propertyName;
}
public String get() {
if(properties1.containsKey(propertyName)) {
return properties1.getProperty(propertyName);
}
return properties2.getProperty(propertyName);
}
}
The following usage:
public class InjectorFactoryTest {
public static void main(String ... parameters) {
System.out.println(InjectorFactory.getProperty("property1"));
System.out.println(InjectorFactory.getProperty("property2"));
System.out.println(InjectorFactory.getProperty("property3"));
}
}
Outputs:
Property1Value
Property2Value
Property3Value

Spring JUnit ApplicationContextInitializer parameters

We have implemented an extended .properties format. Such a properties file can contain an optional include property. The value of this properties is the classpaths of other properties files to load recursively.
I could configure the Spring environment for my application but I have a problem to engage this mechanism with SpringJUnit4ClassRunner.
I thought I could use the initializer property of the ContextConfiguration annotation but it looks it can only be instantiated with a no-arg constructor.
I need to give it the root file of my properties files hierarchy. It could eventually be another annotation on my test class, but again, how can I access it ?
The only think I have found so far is to set this file as a system property in my test class static initializer. ugly?:
#ActiveProfiles("qacs.controller.channels=mock")
#ContextConfiguration(initializer=ContainerTestContextInitializer.class)
public class QacsControllerTest
{
static
{
System.setProperty(ContainerTestContextInitializer.SYSTEM_PROPERTY, "classpath:com/xxx/qacs/QacsControllerTest.properties");
}
#Test
void test() {}
}
}
public class ContainerTestContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>
{
public static final String SYSTEM_PROPERTY = "icomp.test.properties";
#Override
public void initialize(ConfigurableApplicationContext pApplicationContext)
{
String path = System.getProperty(SYSTEM_PROPERTY);
if (path == null)
{
throw new IllegalStateException("Missing system property " + SYSTEM_PROPERTY);
}
final DefaultPropertiesLoader loader;
loader = new DefaultPropertiesLoader(System.getProperties());
try
{
loader.load(path);
}
catch (IOException e)
{
throw new IllegalStateException(e.getMessage(), e);
}
MutablePropertySources sources = pApplicationContext.getEnvironment().getPropertySources();
MapPropertySource mps = new MapPropertySource(Launcher.ICOMP_PROPERTY_SOURCE, (Map) loader.getProperties());
sources.addFirst(mps);
}
}

How can I inject a properties file with JDK only?

Here is my class:
public class Foo {
private static Properties conf;
}
If I manually load conf, I could do it this way (error handling code ommitted for the sake of clarity):
public class Foo {
private static Properties conf;
static {
InputStream is =... // Get the properties file as Stream from classpath
conf = new Properties();
conf.load(is);
// ...
}
}
Now, I want to remove all this garbage for something like this:
public class Foo {
#javax.annotation.Resource(name="my-file-on-classpath.properties")
private static Properties conf;
}
I know I could do it with Spring or anything similar.
However, is it possible to accomplish this by using the JDK only ?
Usage of CDI requires a CDI container. This is not included in JavaSE. You have to use an additional one like Spring for example. It's defined in JavaEE 6.

Servlet init and Class

I actually have a programm with a servlet :
#WebServlet("/Controler")
public class Controler extends HttpServlet {
}
I need to use a property file : file.properties in my program. To load it, I have a class :
public class PropLoader {
private final static String m_propertyFileName = "file.properties";
public static String getProperty(String a_key){
String l_value = "";
Properties l_properties = new Properties();
FileInputStream l_input;
try {
l_input = new FileInputStream(m_propertyFileName); // File not found exception
l_properties.load(l_input);
l_value = l_properties.getProperty(a_key);
l_input.close();
} catch (Exception e) {
e.printStackTrace();
}
return l_value;
}
}
My property file is in the WebContent folder, and I can access it with :
String path = getServletContext().getRealPath("/file.properties");
But I can't call theses methods in another class than the servlet...
How can I access to my property file in the PropLoader class ?
If you want to read the file from within the webapp structure, then you should use ServletContext.getResourceAsStream(). And of course, since you load it from the webapp, you need a reference to the object representing the webapp: ServletContext. You can get such a reference by overriding init() in your servlet, calling getServletConfig().getServletContext(), and pass the servlet context to the method loading the file:
#WebServlet("/Controler")
public class Controler extends HttpServlet {
private Properties properties;
#Override
public void init() {
properties = PropLoader.load(getServletConfig().getServletContext());
}
}
public class PropLoader {
private final static String FILE_PATH = "/file.properties";
public static Properties load(ServletContext context) {
Properties properties = new Properties();
properties.load(context.getResourceAsStream(FILE_PATH));
return properties;
}
}
Note that some exceptions must be handled.
Another solution would be to put the file under WEB-INF/classes in the deployed webapp, and use the ClassLoader to load the file: getClass().getResourceAsStream("/file.properties"). This way, you don't need a reference to ServletContext.
I would recommend to use the getResourceAsStream method (example below). It would need that the properties file be at the WAR classpath.
InputStream in = YourServlet.class.getClassLoader().getResourceAsStream(path_and_name);
Regards
Luan

Make Java Properties available across classes?

I chose to take properties file for customization of some settings.
I use the following code to make a Properties Object available in a class
Properties defaultProps = new Properties();
try {
FileInputStream in = new FileInputStream("custom.properties");
defaultProps.load(in);
in.close();
} catch (Exception e) {
e.printStackTrace();
}
Do I have to add this to every class? Probably not because then every class would open a stream to this file.
But I'm not sure how to handle this properly.
Should I make a class MyProperties and instantiate it in whatever class needs properties?
Thanks in advance!
Once you initialized defaultProps, you can make its contents available to other objects in your app e.g. via a public static accessor method, e.g.:
public class Config {
private static Properties defaultProps = new Properties();
static {
try {
FileInputStream in = new FileInputStream("custom.properties");
defaultProps.load(in);
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getProperty(String key) {
return defaultProps.getProperty(key);
}
}
This is the simplest approach, however it creates an extra dependency which makes unit testing harder (unless you provide a method in Config to set a mock property object for unit testing).
An alternative is to inject defaultProps (or individual configuration values from it) into each object which needs it. However, this may mean you need to add extra parameter(s) to lots of methods if your call hierarchies are deep.
If you only need one instance of your properties class you can use the singleton (anti?)-pattern.
It would look like a class like this:
public class MyProperties extends Properties {
private static MyProperties instance = null;
private MyProperties() {
}
public static MyProperties getInstance() {
if (instance == null) {
try {
instance = new MyProperties();
FileInputStream in = new FileInputStream("custom.properties");
instance.load(in);
in.close();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
return instance;
}
}
Why not use a static ResourceBundle ?
static final ResourceBundle myResources =
ResourceBundle.getBundle("MyResources", currentLocale);
There's too little information to determine what the best way to handle this would be. You may want to expose it using an accessor, or pass it into each class that requires it. Alternatively, you may pull out the properties that each class needs and pass their values into the class's constructor.
Load the properties once using and store the Properties somewheres that others classes can pull from. If that is a MyProperties class that references a static variable somewhere that is fine.
This is a special case of making anything available globally. Using static methods is quite bad. A better but bad solution is using the sigleton pattern. Testing is the greatest problem here. IMHO, the best way is using Dependency injection, although it may be an overkill for small applications.
Since this information is static across all instances, I recommend implementing the Properties class as a singleton. By using the static initialization block method, you can have it load the file automatically when the program starts up.
public class Properties {
static {
try {
FileInputStream in = new FileInputStream("custom.properties");
load(in);
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
protected static void load(FileInputStream in) {
// existing load functionality here
}
}
You are still going to need an internal storage mechanism and accessor mechanism. These should also be marked static.
Rather than loading properties in every class. Load it somewhere around main() and pass it to other classes via their constructors.
Don't share them globally.
- Difficult to test
- Against the abstraction (Global access, DAO can access user settings. it should be prevented by passing only what it needs.. not everything)
- Classes lie what they need

Categories

Resources