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
Related
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.
I am looking how to use the Java default properties value in XML without specifying in application YML or what ever.
This is my java configuration and default I want to use this URL value until providing it from YML file.
#EnableConfigurationProperties
#ConfigurationProperties(prefix = "test.sample")
public #Data class SampleProperties {
private String serverurl ="test.example.com";
}
When I try to use in XML
<property name="serverURL" value="${test.sample.serverurl}" />
Throwing
IllegalArgumentException : Could not resolve placeholder 'test.sample.serverurl' in value "${test.sample.serverurl}"
Your use of the placeholder in XML does not include a default value to use when it is missing
Default values can be provided with a :default-value suffix on the placeholder
<property name="serverURL" value="${test.sample.serverurl:http://localhost}" />
The example is complicated by the : in the default value, simpler ones might be
value="example:default"
value="test.sample.port:8080"
There is a probable duplicate Is there a way to specify a default property value in Spring XML?. Here is a decent tutorial on properties 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
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.
I wish to expose a Properties Spring bean whose values have been expanded via the typical property expansion mechanism. I'm using Spring 3.1. Let me digress.
Given the following properties file:
server.host=myhost.com
service.url=http://${server.host}/some/endpoint
And this portion of Spring XML config file:
<bean id="appProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations">
<list>
<value>classpath:default.properties</value>
</list>
</property>
</bean>
<context:property-placeholder properties-ref="appProperties" />
I can write the following working code:
#Component
public class MyComponent {
#Autowired
#Qualifier("appProperties")
private Properties appProperties;
#Value("${service.url}")
private String serviceUrl;
// remainder omitted
}
The only problem is that if I obtain the service.url value from appProperties I get http://${server.host}/some/endpoint - ie the value is unexpanded. However, if I get the value of service.url from serviceUrl, the value has been expanded: http://myhost.com/some/endpoint.
Does anyone know of a good way to expose a Properties instance as a Spring bean whose values have been expanded?
Alternatively, if anyone can point me to a Spring bean (must be Spring 3.1) that will do the expansion for me, I'll accept this too! (Interestingly, if you manually pull the property values from the Environment or PropertySource you'll find that these too are unexpanded.)
Thanks,
Muel.
I'm pretty late to this, but it's been viewed enough times that i figured it warranted a quick response. What you're seeing is an artifact of how Spring handles property expansion. When the need for property expansion is found, only previously loaded sources are checked, not the currently loading property source. When the file loads, there are no previous sources, so ${server.host} does not expand. When you later reference ${server.url} via the #Value annotation, the property file source is loaded and can be searched as you expected. This is why the #Value annotation gets full expansion but the result queried from the property file does not.