Spring load command line arguments from xml application context - java

How can I use varargs from Spring application context xml?
java -jar my.jar --variable=value
application-context.xml
<bean id="fooClassInstance" class="my.package.FooClass">
<property name="myproperty" value="${variable}" />
</bean>

Try to use System properties:
java -Dvariable=value -jar my.jar
That either works out of the box or you need to tell the app context to look at system properties when it expands variables. It's been a while since I tried that.
A good starting point is PropertySourcesPlaceholderConfigurer.

When you do something like this in your Spring XML config
<bean id="fooClassInstance" class="my.package.FooClass">
<property name="myproperty" value="${variable}" />
</bean>
Spring uses PropertyPlaceholderConfigurer to search for those variables in System/enviroment variables and/or list of predefined properties files.
So the easiest way is to pass that value as a system or environment variable with -Dvariable=value.
If you want to pass those values as arguments into min you can still do hacks like
public static void main(String[] args) {
// parse arguments into key, value pairs
System.setProperty(<key>, <value>);
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(<your XML config file>);
// use Spring context to get beans
}

Related

What is the Best way to get the values from the Properties File in Spring?

I have used the following ways to get the values from the properties. But I would like to know which one of these is the best to use to follow the coding standard? Also, are there any other ways that we can get the values from the properties file in Spring?
PropertySourcesPlaceholderConfigurer
getEnvironment() from the Spring's Application Context
Spring EL #Value
Along with the other configuration classes (ApplicationConfiguration etc.) I create a class with the annotation #Service and here I have the following fields to access the properties in my file:
#Service
public class Properties (){
#Value("${com.something.user.property}")
private String property;
public String getProperty (){ return this.property; }
}
Then I can autowire the class and get the properties from my properties file
The answer is,
it depends.
If the properties are configuration values,
then configure a propertyConfigurer
(below is an example for a Spring xml configuration file).
<bean id="propertyConfigurer"
class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<value>classpath:configuration.properties</value>
<value>classpath:configuration.overrides.properties</value>
</list>
</property>
</bean>
When configured this way,
the properties from the last file found override those found earler
(in the locations list).
This allows you to ship the standard configuration.properties file bundled in the war file and store a configuration.overrides.properties at each installation location to account for installation system differences.
Once you have a propertyConfigurer,
annotate your classes using the #Value annotation.
Here is an example:
#Value("${some.configuration.value}")
private String someConfigurationValue;
It is not required to cluster the configuration values into one class,
but doing so makes it easier to find where the values are used.
#Value will be the simple and easy way to use, as it will inject value from property file to your field.
Both the older PropertyPlaceholderConfigurer and the new PropertySourcesPlaceholderConfigurer added in Spring 3.1 resolve ${…} placeholders within bean definition property values and #Value annotations.
unlike getEnvironment
using property-placeholder will not expose the properties to the
Spring Environment – this means that retrieving the value like this
will not work – it will return null
when you are using <context:property-placeholder location="classpath:foo.properties" /> and you use env.getProperty(key); it will always return null.
see this post for the problem using getEnvironment : Expose <property-placeholder> properties to the Spring Environment
Moreover, in Spring Boot you can use #ConfigurationProperties to define your own properties with hierarchical and type-safe in application.properties. and you don't need to put #Value for every field.
#ConfigurationProperties(prefix = "database")
public class Database {
String url;
String username;
String password;
// standard getters and setters
}
in application.properties:
database.url=jdbc:postgresql:/localhost:5432/instance
database.username=foo
database.password=bar
Quote from : properties with spring

Issue with loading multiple properties files with Spring <util:properties /> when the list of files is a parameter

We need to load multiple properties files together and use them as one source of properties. <util:properties> allows you to pass a comma separated list of files and everything works fine so far. So, the following is good:
<util:properties loaction="conf/file1.properties,conf/file2.properties,abc-*.properties" />
However, in our case, the list of properties file is not fixed and it comes from another master properties file that is loaded before. We want to pass that list to <util:properties> as a parameter but it doesn't work.
<util:properties location="${allPropertiesFiles}" />
Where ${allPropertiesFiles} is defined as
allPropertiesFiles=conf/file1.properties,conf/file2.properties,abc-*.properties
This fails because of commas in the list of files. It treats them as one single file name and throws FileNotFoundException.
I wonder at what point Spring tries to split the files by comma and it looks like that it happens before resolving ${allPropertiesFiles}. For example if I do as below it works fine, but that is not a practical solution for us as we don't know how many files are included in that list.
<util:properties location="${propFile.location1},${propFile.location2},${propFile.location3}" />
UPDATE:
It seems to be a Spring issue with processing and splitting with ',' before resolving the property value in ${...}. I even tried using Spring EL to split it but it fails again with parsing the valid EL, because it first breaks it based on ',' then evaluates the expression. Below example fails with EL parse exception:
<util:properties location="#{'${allPropertiesFiles}'.split(',')}" />
FYI this observation is with Spring 4.2.x. Any suggestion is much appreciated.
Have you tried to surround your variable value with double quotes?
In a small test, this worked like a charm:
ApplicationContext:
<bean id="test" class="de.de.proptest.Test">
<property name="p" ref="props" />
</bean>
<util:properties location="${props}" id="props" />
I have 2 properties files, a.properties with content:
a=1
and b.properties with content:
b=2
With the JVM argument
-Dprops="file:/home/dominik/sandbox/proptest/a.properties, file:/home/dominik/sandbox/proptest/b.properties"
I get the following output (cut to the interesting points):
Mar 11, 2017 1:32:11 PM org.springframework.beans.factory.config.PropertiesFactoryBean loadProperties
INFO: Loading properties file from URL [file:/home/dominik/sandbox/proptest/a.properties]
Mar 11, 2017 1:32:11 PM org.springframework.beans.factory.config.PropertiesFactoryBean loadProperties
INFO: Loading properties file from URL [file:/home/dominik/sandbox/proptest/b.properties]
Test [p={b=2, a=1}]
So, I found a solution/workaround to my question and would like to share it here.
The bean parsers of util:properties and context:property-placeholder split the given string value for location property by ,. It happens way before property resolution occurs. Therefore if you want to pass a comma-separated list of properties files to these beans it just won't work.
So, instead of using <util:properties> I decided to use PropertiesFactoryBean directly and set the location as a parameter. This fixes how the bean definition is built in spring, beacuse it uses Spring default bean parser. Then I provided a custom version of Spring ResourceArrayPropertyEditor which converts a String to Resource[]. The property editor handles comma-separated strings while converting to Resource[].
Here is my context xml now:
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="org.springframework.core.io.Resource[]" value="com.mycompany.CustomResourceArrayPropertyEditor"/>
</map>
</property>
</bean>
<bean id="allPropertiesFromFiles" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations" value="${propertiesFile.locations}" />
</bean>
And here is my custom PropertyEditor:
public class CustomResourceArrayPropertyEditor extends ResourceArrayPropertyEditor
{
private final ResourcePatternResolver resourcePatternResolver;
public CustomResourceArrayPropertyEditor()
{
this(new PathMatchingResourcePatternResolver());
}
public CustomResourceArrayPropertyEditor(ResourcePatternResolver resourcePatternResolver)
{
super(resourcePatternResolver, null);
this.resourcePatternResolver = resourcePatternResolver;
}
#Override
public void setAsText(String text)
{
String pattern = resolvePath(text).trim();
String[] resourceNames = StringUtils.commaDelimitedListToStringArray(pattern);
List<Resource> resourceList = new ArrayList<Resource>();
try {
for (String resourceName: resourceNames)
{
Resource[] resources = resourcePatternResolver.getResources(resourceName);
for (Resource res: resources)
resourceList.add(res);
}
}
catch (IOException ex) {
throw new IllegalArgumentException("Could not resolve resource location pattern [" + pattern + "]", ex);
}
setValue(resourceList.toArray(new Resource[0]));
}
}
The other workaround that I could think of was to create a BeanFactoryPostProcessor to visit beans and update bean definitions of util:properties and context:propery-placeholder to simply use TypedStringValue for locations property as opposed to String[].
Hope it helps.

Syntax for specifying spring xml files in ClassPathXmlApplicationContext

I am trying to find out the syntax for specifying the spring's XML file in the constructor of ClassPathXmlApplicationContext. By syntax I don't mean the method signature but the actual string
For example the following three work.
ApplicationContext context = new ClassPathXmlApplicationContext("com/anshbansal/alarm/alarm.xml");
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:alarm.xml");
ApplicationContext context = new ClassPathXmlApplicationContext("alarm.xml");
I have googled and tried to go through the Spring 3.1.0 source code also. But I got stuck after doResolvePlaceholders method in org.springframework.core.env.AbstractPropertyResolver class of Spring. Specificaly I was not able to understand how placeholders are resolving to the path.
Can anyone share what is the syntax for the string to specify the xml file?
EDIT
I mean the syntax to specify path to spring xml file like in the constructor. I do not mean the syntax of the xml file itself.
Ok, I understand the question now :-). PropertyResolver is used only to put environment values (or property file values) into spring XML file, i.e.:
<context:property-placeholder location="file:///some/path/file.properties"/>
and then resolving them inside this spring xml, i.e.:
<bean id="mailInviteMessage" class="org.springframework.mail.SimpleMailMessage">
<property name="from" value="${mail.from}"/>
<property name="subject" value="${mail.subject}"/>
</bean>
Putting spring XML in the classpath and naming in standard convention (i.e. beans.xml) is preferable. However, you may configure it putting a parameter to java invocation, i.e.
java -Dmy.parameter=/path/to/beans.xml ...
and then loading it manually:
Context context = new FileSystemXmlApplicationContext(System.getProperty("my.parameter"));

Spring putting dynamically generated values into placeholders

I am new to Spring. I now understand how to use placeholders to read values from a properties file:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:properties.txt"/>
</bean>
<int-mqtt:outbound-channel-adapter id="mqtt-publish"
client-id="${clientID}"
client-factory="clientFactory"
auto-startup="true"
url="${url}"
default-qos="${qos}"
default-retained="${retain}"
default-topic="${topic}" />
Everything works fine with the code above... But... Is it possible for instance to replace the clientID by something generated at runtime (or from user input) instead of statically reading it from a properties file?
By runtime, do you mean dynamically for each message?
In that case, no, because the clientId is used while establishing the connection, which is done once (or when the connection to the server is lost).
If you mean to provide a dynamic value programmatically when the application context initializes, then, yes, the Spring Expression Language is the solution.
For example, #{myBean.myProperty} will call the getMyProperty() method on a bean myBean and #{myBean.someMethod()} will invoke someMethod().
Also see the dynamic-ftp sample, which uses placeholders at runtime by creating a new outbound adapter on demand using property placeholders, in a child application context.

Import Spring config file based on property in .properties file

In my Spring xml configuration I'm trying to get something like this to work:
<beans>
<import resource="${file.to.import}" />
<!-- Other bean definitions -->
</beans>
I want to decide which file to import based on a property in a properties file.
I know that I can use a System property, but I can't add a property to the JVM at startup.
Note: The PropertyPlaceHolderConfigurer will not work. Imports are resolved before any BeanFactoryPostProcessors are run. The import element can only resolve System.properties.
Does anyone have a simple solution to this? I don't want to start subclassing framework classes and so on...
Thanks
This is, unfortunately, a lot harder than it should be. In my application I accomplished this by doing the following:
A small, "bootstrap" context that is responsible for loading a PropertyPlaceholderConfigurer bean and another bean that is responsible for bootstrapping the application context.
The 2nd bean mentioned above takes as input the "real" spring context files to load. I have my spring context files organized so that the configurable part is well known and in the same place. For example, I might have 3 config files: one.onpremise.xml, one.hosted.xml, one.multitenant.xml. The bean programmatically loads these context files into the current application context.
This works because the context files are specified as input the the bean responsible for loading them. It won't work if you just try to do an import, as you mentioned, but this has the same effect with slightly more work. The bootstrap class looks something like this:
public class Bootstrapper implements ApplicationContextAware, InitializingBean {
private WebApplicationContext context;
private String[] configLocations;
private String[] testConfigLocations;
private boolean loadTestConfigurations;
public void setConfigLocations(final String[] configLocations) {
this.configLocations = configLocations;
}
public void setTestConfigLocations(final String[] testConfigLocations) {
this.testConfigLocations = testConfigLocations;
}
public void setLoadTestConfigurations(final boolean loadTestConfigurations) {
this.loadTestConfigurations = loadTestConfigurations;
}
#Override
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
context = (WebApplicationContext) applicationContext;
}
#Override
public void afterPropertiesSet() throws Exception {
String[] configsToLoad = configLocations;
if (loadTestConfigurations) {
configsToLoad = new String[configLocations.length + testConfigLocations.length];
arraycopy(configLocations, 0, configsToLoad, 0, configLocations.length);
arraycopy(testConfigLocations, 0, configsToLoad, configLocations.length, testConfigLocations.length);
}
context.setConfigLocations(configsToLoad);
context.refresh();
}
}
Basically, get the application context, set its config locations, and tell it to refresh itself. This works perfectly in my application.
Hope this helps.
For the Spring 2.5 and 3.0, I have a similar solution to louis, however I've just read about 3.1's upcoming feature: property management, which sounds great too.
There is an old issue on the Spring JIRA for adding properties placeholder support for import (SPR-1358) that was resolved as "Won't Fix", but there has since been a proposed solution using an EagerPropertyPlaceholderConfigurer.
I've been lobbying to have SPR-1358 reopened, but no response so far. Perhaps if others added their use cases to the issue comments that would help raise awareness.
Why not:
read your properties file on startup
that will determine which Spring config to load
whichever Spring config is loaded sets specific stuff, then loads a common Spring config
so you're effectively inverting your current proposed solution.
Add something similar to the following:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreResourceNotFound"><value>true</value></property>
<property name="locations">
<list>
<value>classpath:propertyfile.properties</value>
</list>
</property>
</bean>
If what you want is to specify the imported XML file name outside applicationContext.xml so that you could replace applicationContext.xml without losing the configuration of the imported XML file path, you can just add an intermediate Spring beans XML file, say, confSelector.xml, so that applicationContext.xml imports confSelector.xml and confSelector.xml only contains an <import> element that refers to the suitable custom beans XML file.
Another means that might be of use are XML entities (defined by adding <!ENTITY ... > elements into the DTD declaration at the beginning of XML). These allow importing XML fragments from other files and provide "property placeholder"-like functionality for any XML file.
Neither of these solutions allows you to have the configuration file in Java's .properties format, though.
André Schuster's answer, which I bumped, helped me solve a very similar issue I was having in wanting to find a different expression of properties depending on whether I was running on my own host, by Jenkins on our build host or in "real" deployment. I did this:
<context:property-placeholder location="file:///etc/myplace/database.properties" />
followed later by
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>WEB-INF/classes/resources/database.properties</value>
...
</list>
</property>
</bean>
which solved my problem because on my development host, I put a link to my own copy of database.properties in /etc/myplace/database.properties, and a slightly different one on the server running Jenkins. In real deployment, no such file is found, so Spring falls back on the "real" one in resources in my class files subdirectory. If the properties in question have already been specified by the file on /etc/myplace/database.properties, then (fortunately) they aren't redefined by the local file.
Another workaround which does not rely on system properties is to load the properties of all the files using a different PropertyPlaceholderConfigurer for each file and define a different placeholderPrefix for each of them.
That placeholderprefix being configured by the initial property file.
Define the first property file: (containing either first or second)
global.properties
fileToUse=first
Define the files containing a property that can be switched depending on the property defined just above:
first.properties
aProperty=propertyContentOfFirst
second.properties
aProperty=propertyContentOfSecond
Then define the place holders for all the files:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:global.properties</value>
</list>
</property>
</bean>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="placeholderPrefix" value="first{" />
<property name="locations">
<list>
<value>classpath:first.properties</value>
</list>
</property>
</bean>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="placeholderPrefix" value="second{" />
<property name="locations">
<list>
<value>classpath:second.properties</value>
</list>
</property>
</bean>
Use the property defined in global to identify the resource to use from the other file:
${fileToUse}{aProperty}
If I add the JVM argument below and have the file myApplicationContext.dev.xml, spring does load
-DmyEnvironment=dev
<context:property-placeholder />
<import resource="classpath:/resources/spring/myApplicationContext.${myEnvironment}.xml"/>
I'm using Spring 3 and load a properties like that:
<context:property-placeholder location="/WEB-INF/my.properties" />

Categories