Precedence of properties: system vs. deployment descriptor vs. properties file - java

If I have system property which I pass my container (e.g. Tomcat) like the following:
-Dmy.property=myValueOne
and a property with the same key defined in my web.xml:
<context-param>
<param-name>my.property</param-name>
<param-value>myValueTwo</param-value>
</context-param>
... and a property with the same key defined in one of my config*.properties files:
my.property=myValueThree
Which value will this property have?
myValueOne, myValueTwo or myValueThree?
If you have several properties with the same key, is there a hierarchy which defines which kind of property overwrites which other kind of property?

Simply said all three will be available and you can use SpEL to obtain the value of each.
#{systemProperties['my.property']} // myValueOne
#{servletContextInitParams['my.property'] // myValueTwo
The properties depend on how you are loading them (a #PropertySource or `
However you probably want to know what happens if you use a placeholder and you have the situation you have.
<property name="myProperty" value="${my.property}" />
Assuming you use the default (and are on Spring 3.1 or up) the following resources are consulted in order, in a web based environment.
Property Files
ServletConfig Init Params (
ServletContext Init Params
Jndi
System Properties (specified by -D)
Environment Properties
So in your case the ${my.property} will resolve to myValueThree. That is if you preserve the defaults. If you specify local-override="true" the property files are consulted last and the value would be myValueTwo.
Links
StandardServletEnvironment javadoc
StandardEnvironment javadoc
PropertySourcesPlaceholderConfigurer javadoc

Related

brokerURL syntax not clear to me

Can some one help me to understand this syntax on this ActiveMQConnectionFactory bean ? What are the 3 options for? what is the logic?
DU_MESSAGING_SERVER_URL (system param on our system)
MESSAGING_SERVER_URL (system param on our system)
tcp://localhost:61917
<property name="brokerURL" value="${DU_MESSAGING_SERVER_URL:${MESSAGING_SERVER_URL:tcp://localhost:61917}}"/>
This syntax using ${} is for loading system or environmental properties into a configuration file.
This is useful in situations where you might not want to modify the configuration file but rather just set a system property (e.g. moving between a test environment and prod). You just put the name of the system property in the configuration file and the value of that property will be substituted when the configuration is loaded. For example, you could use ${foo} in your config file and then define this system property -Dfoo=bar and then the value bar would be used in your config file.
The : character defines a default value if the system property is not set. For example, you could use ${foo:defaultFoo} in your config file and then if the foo system property was not defined then the value defaultFoo would be used in your config file.
In your situation these ideads are being nested together. So if you use ${DU_MESSAGING_SERVER_URL:${MESSAGING_SERVER_URL:tcp://localhost:61917}} then the value of the DU_MESSAGING_SERVER_URL system property will be used in the configuration. If DU_MESSAGING_SERVER_URL is not defined then ${MESSAGING_SERVER_URL:tcp://localhost:61917} will be evaluated which will use the value of the MESSAGING_SERVER_URL system property in the configuration. If MESSAGING_SERVER_URL is not defined then tcp://localhost:61917 will be used.

Unable to resolve property placeholder [duplicate]

I have a Spring 3.1 application. Let's say it has an XML with the following content:
<context:property-placeholder location="classpath:somename.properties" />
<context:property-placeholder location="classpath:xxx.properties" />
I would like some.properties to be always loaded (let's assume it exists), but the xxx part of the second place holder to be replaced by some name depending on the active profile. I've tried with this:
<beans profile="xx1">
<context:property-placeholder location="classpath:xx1.properties" />
</beans>
<beans profile="xx2">
<context:property-placeholder location="classpath:xx2.properties" />
</beans>
Also, both files have properties with the same key but different value.
But it didn't work as some later bean that has a placeholder for one property whose key is defined in xx1.properties (and xx2.properties) makes Spring complain that the key is not found in the application context.
You can do:
<context:property-placeholder location="classpath:${spring.profiles.active}.properties" />
It works fine, but is perhaps not adapted when using multiple profiles in the same time.
When declaring 2 property placeholders, if the 1st one does not contain all the applications keys, you should put the attribute ignoring unresolvable = true, so that the 2nd placeholder can be used.
I'm not sure if it is what you want to do, it may if you want both xx1 and xx2 profiles be active in the same time.
Note that declaring 2 propertyplaceholders like that make them independant, and in the declaration of xx2.properties, you can't reuse the values of xx1.properties.
If you need something more advanced, you can register your PropertySources on application startup.
web.xml
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>com.xxx.core.spring.properties.PropertySourcesApplicationContextInitializer</param-value>
</context-param>
file you create:
public class PropertySourcesApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final Logger LOGGER = LoggerFactory.getLogger(PropertySourcesApplicationContextInitializer.class);
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
LOGGER.info("Adding some additional property sources");
String profile = System.getProperty("spring.profiles.active");
// ... Add property sources according to selected spring profile
// (note there already are some property sources registered, system properties etc)
applicationContext.getEnvironment().getPropertySources().addLast(myPropertySource);
}
}
Once you've done it you just need to add in your context:
<context:property-placeholder/>
Imho it's the best way to deal with spring properties, because you do not declare local properties everywhere anymore, you have a programmatic control of what is happening, and property source xx1 values can be used in xx2.properties.
At work we are using it and it works nicely. We register 3 additional property sources:
- Infrastructure: provided by Puppet
- Profile: a different property loaded according to the profile.
- Common: contains values as default, when all profiles share the same value etc...
I have decided to submit and answer to this as it has not yet been accepted. It may not be what you are looking for specifically but it works for me. Also note that i am using the new annotation driven configuration however it can be ported to the xml config.
I have a properties file for each environment(dev.properties, test.properties etc)
I then have a RootConfig class that is the class that is used for all the configuration. All that this class has in it is two annotations: #Configuration and #ComponentScan(basePackageClasses=RootConfig.class).
This tells it to scan for anything in the same package as it.
There is then a Configuration Containing all my normal configuration sitting wherever. There is also a configuration for each environment in the same package as the root configuration class above.
The environment specific configurations are simply marker classes that have the following annotations to point it to the environment specific properties files:
#Configuration
#PropertySource("classpath:dev.properties")
#Import(NormalConfig.class)
#Profile("dev")
The import tells it to bring in the normal config class. But when it gets in there it will have the environment specific properties set.

How to set the Profile using application.properties in Spring?

I would like to set the Profile using application.properties file with the entry:
mode=master
How to set spring.profiles.active in my context.xml file? init-param works only in a web.xml context.
<init-param>
<param-name>spring.profiles.active</param-name>
<param-value>"${mode}"</param-value>
</init-param>
There are a few ways to change active profiles, none of which take directly from a properties file.
You can use the <init-param> as you are doing in your question.
You can provide a system parameter at application startup
-Dspring.profiles.active="master"
You can get the ConfigurableEnvironment from your ApplicationContext and setActiveProfiles(String...) programmatically with context.getEnvironment().setActiveProfiles("container");
You can use an ApplicationListener to listen to context initialization. Explanations on how to do that here. You can use a ContextStartedEvent
ContextStartedEvent event = ...; // from method argument
ConfigurableEnvironment env = (ConfigurableEnvironment) event.getApplicationContext().getEnvironment();
env.setActiveProfiles("master");
You can get the value "master" from a properties file as you see fit.
You can use either a environment variable, system variable (-D option for the JVM or application) or put it in JNDI (java:comp/env/. You cannot however put it in a properties file, as it is needed before the that specific properties file is read.
There is more information in the #Profile javadocs.
Another solution is to create your own ApplicationContextInitializer implementation which reads a certain file and activates the given profile.
You also can achieve this indirectly via System.setProperty:
// spring.profiles file: profile1,profile2
String anotherProfiles = Files.readString(Path.of("spring.profiles")); // or any other file
// Even some logic can be applied here to anotherProfiles
System.setProperty("spring.profiles.include", "dev," + anotherProfiles)
This sample can be rewritten a bit to read your application.properties file and take specified profiles for Spring.

Spring NamespaceHandler and multiple property placeholders

I'm doing as follows:
register a NamespaceHandler (with all spring.x files, the handler is properly located and invoked)
in the Parser registered by the namespace handler, I load an xml file containing bean definitions (rather than defining them programmatically):
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(parserContext.getRegistry());
reader.loadBeanDefinitions(new ClassPathResource("definitions.xml"));
definitions.xml contains a <context:property-placeholder location="classpath:.. />
the applicationContext.xml which invokes my handler needs to pass a property (e.g. <foo:bar prop="${baz}" />
in the namespace handler I define an inline PropertySource and register it with the Environment, so that I can dynamically register a property that I need, which is based on the prop passed as attribute. I tried registering a String bean instead, but resolution fails.
The property placeholder resolution happens after bean definitions, so happens after my code in the namespace handler's parser is invoked.
However, this all fails. Multiple times, for multiple reasons. Here are they:
if <context:property-placeholder /> is defined without ignore-unresolvable="true" and order, the first placeholder configurer fails to find properties required by the 2nd one. This is logical, of course, and seems to be mandatory whenever multiple placeholder configurers are used
because the dynamic property is based on the passed prop, it looks like file://${prop}/foo", which means it is a nested property. You can't configure the behaviour of nested property resolution per configurer, which means even thoughignore-unresolvable` is true, nested properties are not ignored, and the whole thing fails.
The solution I found is to get the AbstractEnvironment in the namespace handler's parser, and set the call env.setIgnoreUnresolvableNestedPlaceholders(true)
This looks like a hack though. So my questions are:
how to dynamically register properties that will later be resolved?
how to configure ignoring nested property resolution per configurer?
is there a better way to achieve what I need - namely, include a "bundle" of definitions via a custom namespace, and pass properties (loaded from file) to them?
P.S. Spring improvement request posted: https://jira.springsource.org/browse/SPR-10654

Spring Property Placeholders with String Concatenation

My problem looks simple but I'm not able to resolve it. I have a properties file which contains configuration details of all environments (dev, qa, prod).
Example config.properties:
dev.maxLength=2000
qa.maxLength=4000
We have a parent Properties file which holds the host name, environment mappings.
Example hosts.properties:
host1=dev
host2=qa
The property name host1 is stored in a bean hostname.
<bean id="hostname"
factory-bean="localhostInetAddress"
factory-method="getHostName"/>
To resolve the config properties name I have to join the strings as follows,
${${**hostname**}.maxLength} which should be resolved as ${dev.maxLength}
I tried using SpEL with no success. I am getting Could not resolve placeholder Exception. How can I concatenate a bean value in property place holder? How are dynamic property names constructed?
Spring version 3.2
To concatenate the values parsed from Spring property placeholders, you need to escape their values using single quoutes ('') and wrap the placeholder expressions by a SpEL expression using #{}.
<bean id="myService" class=""com.services.MyService">
...
<property name="endpointAddress" value="#{'${server}' + ':' + '${port}' + '${endpoint}'}" />
</bean>
where:
server = http://domain.host.com
port = 7777
endpoint = /services/myservice
The result would be:
http://domain.host.com:7777/services/myservice
I solved the issue by changing PropertyPlaceholderConfigurer beans to Properties.
<util:properties/> are accessible in SpEL.
Example:
"#{prop[host+'.'+'maxLength']}"
where host is a string bean.
It would be better to have environment specific properties in a file of its own and use Spring Profiles.
For example, I have four xml files just for db configuration, local.db.xml, dev.db.xml, qa.db.xml and prod.db.xml.
Inside each db.xml, I set the profile to the appropriate value.
My local.db.xml has
<beans profile="db.local" .. >
For starting Tomcat, I specify the VM options as follows
-Dspring.profiles.active=db.local

Categories