I'm trying to load all key/value pairs in my properties file.
One approach is that I load all the properties using #Value manually, but for that I should know all the keys.
I cannot do this, since property file may be changed in future to include more set of key/value pairs and I may need to modify code again to accommodate them.
Second approach is that I should some how load the properties file and Iterate over it to load all the key/value pairs without knowing the keys.
Say I have following properties file sample.properties
property_set.name="Database MySQL"
db.name=
db.url=
db.user=
db.passwd=
property_set.name="Database Oracle"
db.name=
db.url=
db.user=
db.passwd=
Here is what I'm trying to do
#Configuration
#PropertySource(value="classpath:sample.properties")
public class AppConfig {
#Autowired
Environment env;
#Bean
public void loadConfig(){
//Can I some how iterate over the loaded sampe.properties and load all
//key/value pair in Map<String,Map<String, String>>
// say Map<"Database MySQL", Map<key,vale>>
// I cannot get individual properties like env.getProperty("key");
// since I may not know all the keys
}
}
Spring stores all properties in Environment.
Environment contains collection of PropertySource. Every PropertySource contains properties from specific source. There are system properties, and java environment properties and many other. Properties from you file will be there as well.
Any source has own name. In your case automatically generated name will be look like "class path resource [sample.properties]". As you see, the name is not so convenient. So lets set more convenient name:
#PropertySource(value="classpath:sample.properties", name="sample.props")
Now you can get source by this name:
AbstractEnvironment ae = (AbstractEnvironment)env;
org.springframework.core.env.PropertySource source =
ae.getPropertySources().get("sample.props");
Properties props = (Properties)source.getSource();
Note that I specified full name of PropertySource class, to avoid conflict with #PropertySource annotation class. After that, you can work with properties. For example output them to console:
for(Object key : props.keySet()){
System.out.println(props.get(key));
}
You can autowire in an EnumerablePropertySource which contains the method getPropertyNames()
you can look up the class Properties in the jdk api and use the method load(InputStream inStream)
InputStream in = new FileInputStream("your properties location");
Properties prop = new Properties();
prop.load(in);
ps: prop is subClass of HashTable and don't forget to close stream
Related
Does Spring put all properties into the same hashmap/list?
How can I get a list of all of the properties being managed by Spring
For example, if I have two properties files and they define overlapping properties which
// FILE: src/main/resources/file1.properties
prop1.color = red
prop1.fill = green
// FILE: src/main/resources/file2.properties
prop2.color = purple
prop1.fill = ORANGE
The configuration class used to tell Spring to read the two files is something like this:
#PropertySource("classpath:file1.properties")
#PropertySource("classpath:file2.properties")
#Configuration
public class MyConfig {
}
My question is Can I get an iterator to access all of the properties defined? If so how.
Searching for answer
So far I haven't found a solution to this question.
I did an Internet search for "stack overflow Spring properties" and did not found the answer.
Some SO questions I found are:
Spring boot get all properties from properties file and load into hashmap
Spring Boot application.properties
But they didn't answer my question
Spring provides a PropertySources interface which is implemented by the MutablePropertySources class. This class basically just holds a list of PropertySource instances (e.g. instances of the PropertiesPropertySource class - not to be confused with the #PropertySource annotation) and provides methods for iterating over these instances.
If you look at the implementation of MutablePropertySources you can see that there are multiple methods for adding PropertySource instances at different positions and relative to other PropertySources in that list. If you want to get the value of a property, you can call the get method of a MutablePropertySources instance with the name of that property. The get method then iterates over all PropertySources in its list and tries to find a property with the given name.
Then there is the Environment interface that is implemented by the abstract class AbstractEnvironment. You can use the latter to access the PropertySources instance that is used by that environment.
For example, for testing purposes, you can do the following to print out all properties and their values that are managed by MapPropertySources - a subtype of PropertySource.
#PropertySource("classpath:file1.properties")
#PropertySource("classpath:file2.properties")
#Configuration
public class ExampleConfig {
#Autowired
public ExampleConfig(Environment environment) {
AbstractEnvironment env = (AbstractEnvironment) environment;
for (org.springframework.core.env.PropertySource<?> source : env.getPropertySources()) {
if (source instanceof MapPropertySource mapPropertySource) {
System.out.println(mapPropertySource.getSource());
}
}
}
}
You can check out the org.springframework.core.env package which contains all of these classes.
So, to answer your first answer more explicitly: No, Spring does not put all properties into a single map. It puts properties from different locations into different PropertySources which are then added to a PropertySources instance.
I have to develop one desktop base application(it has no request/response) and it has many classes.
my Main/Controller class read (*.properties) file,
like
allExtensions = properties.getProperty("ReportFileExtension");
and i have mention some file extension in .properties file like ReportFileExtensionn = .pdf,.doc etc.
This key read from .properties file in Main class and i want use this key value in other class without passing argument in any method or constructor.
is it spring provide a local storage ? so i can use to store attribute and use it other class.
Thanks in Advc.
Your question is bit confusing but based on comments I think you are struggling to understand how to get keys from properties.
Check PropertiesLoaderUtils
Resource resource = new ClassPathResource("/my.properties");
Properties props = PropertiesLoaderUtils.loadProperties(resource);
Now iterate over the props object
Try this
public Set<Object> getAllKeys(){
Set<Object> keys = prop.keySet();
return keys;
}
Or this
public void printProperties() {
for(Entry<Object, Object> e : props.entrySet()) {
System.out.println(e);
}
}
Properties are available globally but if you want you can create a static map and cache the properties in it.
I have a few classes that need some environment specific configuration. I turned to using properties files, which are loaded in the constructor of the class.
public class MyClass {
public MyClass() {
try {
ValidatedEnvironmentProperties props = new ValidatedEnvironmentProperties();
props.load(MyClass.class.getResourceAsStream("/myclass.properties"));
ValidatedEnvironmentProperties extends Properties. Basically, it uses a Java System Property to set a key prefix. I set the System property to, say, production, and in the properties files, I have staging.url=... and production.url=.... This allows me build/runtime selection of which configuration is used while not needing to change property file name.
myclass.properties is stored in src/main/resources.
That works fine, and I rather like the how it works. My problem is that I'm sort of stuck with TestNG. I want to test a bunch of other properties in TestNG unit tests. This led me to create src/test/resources/myclass.properties. Instead of "environment" keys, I use test names like bad_url_test.url=this_ain't_a_url.
I was thinking that src/test/resources would get a higher priority in the classpath/classloader (terminology?), causing the test-specific properties to load. Then, for my various tests, I just set the Java System property to bad_url_test, instantiate MyClass, test my assertions, and then set the System property to a new test, instantiate a new object, and repeat.
I believe the source of the problem is this line in MyClass's constructor:
props.load(MyClass.class.getResourceAsStream("/myclass.properties"));
In MyClassTest, I put these lines to try to understand what's happening:
File f = new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation().getPath());
System.out.println("MyClass classpath: " + f.toString());
f = new File(MyClassTest.class.getProtectionDomain().getCodeSource().getLocation().getPath());
System.out.println("MyClassTest classpath: " + f.toString());
Output is:
MyClass classpath: /home/fandingo/code/project/build/classes/main
MyClassTest classpath: /home/fandingo/code/project/build/classes/test
MyClassTest is correct, but I need something accessible within MyClass's constructor that will automatically prefer /src/test/resources/ when running tests but /src/main/resources/ when running normally.
You need inversion of control. i.e. MyClass needs to be told where to get its properties from and not decide on its own.
There are lots of ways to do this but they all come down to the same idea: MyClass should not know at compile time where its properties come from.
e.g.
In MyClass.java
ValidatedEnvironmentProperties props = new ValidatedEnvironmentProperties();
String resourceSupplierClassName = System.getProperty("resource-supplier-class-name",
MyClass.class.getName());
Class<?> resourceSupplierClass = Class.forName(resourceSupplierClassName);
props.load(resourceSupplierClass.getResourceAsStream("/myclass.properties"));
In MyClassTest.java
System.setProperty("resource-supplier-class-name", MyClassTest.class.getName());
Again, there are many ways to do this. Instead of passing around system properties, etc. you can also use dependency injection (a form of inversion of control). You can change the MyClass constructor to take your props as an argument and then put the onus on the instantiators of MyClass to provide the props or you can use a dependency injection framework such as Spring or Guice to manage creating the appropriate props instance for main/test execution and provide it to objects that need it as needed.
I would like to have properties, that I can reference via #Value in spring beans, that can only be created dependend on other properties.
In particular I am having a property, that describes the file system location of a directory.
myDir=/path/to/mydir
And by convention, there is a file in that directory, that is always called myfile.txt.
Now i want to have access to both, the directory and the file, via #Value annotations inside my beans. And sometimes I want to access them as Strings, sometimes as java.io.Files and sometimes as org.springframework.core.io.FileSystemResource (which by the way works very well out of the box!). But because of that concatenating Strings on demand is not an option.
So what I of course could do is just declare both, but I would end up with
myDir=/path/to/mydir
myFile/path/to/mydir/myfile.txt
and I would like to avoid that.
So I came up with an #Configuration class, that takes the property and adds it as new PropertySource:
#Autowired
private ConfigurableEnvironment environment;
#Value("${myDir}")
private void addCompleteFilenameAsProperty(Path myDir) {
Path absoluteFilePath = myDir.resolve("myfile.txt");
Map<String, Object> props = new HashMap<>();
props.put("myFile, absoluteFilePath.toString());
environment.getPropertySources().addFirst(new MapPropertySource("additional", props));
}
As you can see, in my context I even created a PropertyEditor, that can convert to java.nio.file.Paths.
Now the problem is, that for some reason, this "works on my machine" (in my IDE), but does not run on the intended target environment. There I get
java.lang.IllegalArgumentException: Could not resolve placeholder 'myFile' in string value "${myFile}"
Spring can combine properties
myDir=/path/to/mydir
myFile=${myDir}/myfile.txt
You can also use a default value without defining your myFile in the properties at first:
Properties file
myDir=/path/to/mydir
In class:
#Value("#{myFile:${myDir}/myfile.txt}")
private String myFileName;
Spring expressions can be used to refer the properties.
In my example it was
query-parm=QueryParam1=
query-value=MyParamaterValue
Now while binding them in Spring Bean.
#Configuration
public class MyConfig {
#Value("${query-param}${query-value}")
private String queryString;
}
Above code will inject QueryParam1=MyParamaterValue to the variable queryString.
An instance of java.util.Properties is passed to me which has been created using:
[...]
properties = new java.util.Properties();
try {
properties.load(
AutoProperties.class.getClassLoader().getResourceAsStream(propertyFile)
);
}
[...]
I was wondering how I could retrieve the file name (propertyFile above) from the properties instance? I had a glance at the API and couldn't see any easy way to do it.
The file name (or path name in this case) is not stored in the Properties instance. In fact, you haven't even passed the name to the instance.
You can't. It's not saved in the Properties object.
You cannot get this information, as a Properties object is not necessarily linked to a File...
Indeed, you can populate the Properties in several ways:
Load a properties file (as you did in your example).
Populate directly this object (using put() method from the Hashtable class).