Spring 3.1 Environment does not work with user property files - java

I am doing this..
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(context);
xmlReader
.loadBeanDefinitions(new ClassPathResource("SpringConfig.xml"));
PropertySourcesPlaceholderConfigurer propertyHolder = new PropertySourcesPlaceholderConfigurer();
propertyHolder.setLocation(new ClassPathResource(
"SpringConfig.properties"));
context.addBeanFactoryPostProcessor(propertyHolder);
......
context.refresh();
Now in my #Configuration files, the properties present in my SpringConfig.properties are not getting picked up if I do this...
#Autowired
private Environment env
.....
env.getProperty("my.property")
But I get that property if I use
#Value("${my.property}")
private String myProperty;
I even tried adding couple of more lines like this, but of no use.
ConfigurableEnvironment env = new StandardEnvironment();
propertyHolder.setEnvironment(env);
Does anybody know why my properties are not loaded into Environment? Thanks.

PropertySourcesPlaceholderConfigurer reads property files directly(as it was done by PropertyPlaceholderConfigurer in Spring 3.0 times), it's just a postprocessor which does not change the way properties are used in the Spring context - in this case properties are only available as bean definition placeholders.
It's the PropertySourcesPlaceholderConfigurer who uses Environment and not vice versa.
Property sources framework works on the application context level, while property placeholder configurers only provide the functionality to process placeholders in the bean definitions. To use property source abstraction you should use #PropertySource annotation i.e. decorate your configuration class with something like
#PropertySource("classpath:SpringConfig.properties")
I believe that you can do the same thing programmatically, i.e. you can get the container's ConfigurableEnvironment before the context was refreshed, modify its MutablePropertySources(you need first to get AbstractApplicationContext environment property via context.getEnvironment() ) via getPropertySources().addFirst(new ResourcePropertySource(new ClassPathResource(
"SpringConfig.properties"))); but it's unlikely what you want to do - if you already have a #Configuration annotated class, decorating it with #PropertySource("classpath:SpringConfig.properties") is much simpler.
As for the PropertySourcesPlaceholderConfigurer instance - it will fetch property sources automatically(as it implements EnvironmentAware) from its application context so you need just to register a default instance of it.
For the examples of custom property source implementation see http://blog.springsource.org/2011/02/15/spring-3-1-m1-unified-property-management/

Adding local properties to PropertySourcesPlaceholderConfigurer (with setProperties() or setLocation()) doesn't make them available in the Environment.
Actually it works in the opposite way - Environment acts as a primary source of properties (see ConfigurableEnvironment), and PropertySourcesPlaceholderConfigurer can make properties from the Environment available using ${...} syntax.

I did this per #Boris suggestion..
PropertySourcesPlaceholderConfigurer propertyHolder = new PropertySourcesPlaceholderConfigurer();
ConfigurableEnvironment env = new StandardEnvironment();
env.getPropertySources().addFirst(
new ResourcePropertySource(new ClassPathResource(
"SpringConfig.properties")));
propertyHolder.setEnvironment(env);
context.addBeanFactoryPostProcessor(propertyHolder);
context.register(SpringConfig.class);
context.refresh();
Now in #Configuration classes all properties (including my own and system properties) can be resolved using #Value.
But the Environment that gets #Autowired into #Configuration class has only system properties in it, not SpringConfig.properties I set as above. But clearly, before calling context.refresh() above, the ConfigurableEnvironment has my properties also. But once context.refresh() is called, my properties are removed from the Environment that gets autowired into #Configuration.
I want to be able to use the better syntax, env.getProperty("my.property"). Does anybody know why that is the case?

I am loading 2 types of properties one is the Environment property and the other is the context which you usually get from lets say servletContext.getServletContext().
My environment property is defined as : MOD_CONFIG_ROOT which is set seperately on the environment thereby seperating the location details the ear file which contains code. Here's the configuration.
[ Note: I had to load the property files as first thing before loading the servlets to make use of the properties using ${someProperty} ]
<bean id="externalProperties"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>file:#{ systemEnvironment['MOD_CONFIG_ROOT']
}#{servletContext.contextPath}/users.properties</value>
</list>
</property>
<property name="searchSystemEnvironment" value="true" />
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_FALLBACK" />
</bean>

Related

spring: access properties set from <context:property-placeholder>

I am setting properties using:
<context:property-placeholder location="#{ T(System).getenv().get('DEV_PROPERTIES') ?: 'classpath:/META-INF/properties/config.properties' }"/>
I am able to access the properties:
#Value("${hostname}")
String hostname;`
This works fine.
However, i would like to access the properties using the property map or simple just get the values in a method which can't use #Value variables. Is there a way i can inject the property bean set using <context:property-placeholder />.?
Environment doesn't have the access to properties set from the properties file, it only can read properties from system and environment properties.
No you cannot access the properties used internally by the property-placeholder. What you can do is load the properties into a Properties object and inject that into the property-placeholder and also inject it into whatever you like.
Another tip you don't need to use SpEL to achieve what you want in your location attribute, a simple placeholder would do the trick.
To load a properties object use the util namespace.
<util:properties id="props" location="${DEV_PROPERTIES:classpath:/META-INF/properties/config.properties}" />
<context:property-placeholder properties-ref="props" />
To make the properties you want available to the Environment you should use the #PropertySource annotation on a #Configuration class.
#Configuration
#PropertySource("${DEV_PROPERTIES:classpath:/META-INF/properties/config.properties}")
public class ApplicationConfig { ... }
You can either add this as a bean to your xml file or have it detected while component scanning. Either way should work.

Read all properties files in a directory

Right now I am reading properties file in spring as
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="customer/messages" />
</bean>
Here I am specifying that read messages.properties in customer directory. But what I want to do is to specify a directory and ask spring to read all properties file present in that directory. How can I achieve that?
I tried value="customer/*" but it doesn't work.
Using <context:property-placeholder> is more recommended as:
<context:property-placeholder
locations="classpath:path/to/customer/*.properties" />
You can also do this using Spring 3.1+ Java Config with:
#Bean
public static PropertyPlaceholderConfigurer properties(){
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
Resource[] resources = new ClassPathResource[ ]
{ new ClassPathResource( "path/to/customer/*.properties" ) };
ppc.setLocations( resources );
ppc.setIgnoreUnresolvablePlaceholders( true );
return ppc;
}
You may need to tailor the type of resource to load properties from:
Resource abstract implementations
Built-in resource implementation
A complete walk through of properties in Spring
To use a property, you can use Environment abstraction. It can be injected and used to retrieve property values at runtime.
Finally used approach given on following blog -
http://rostislav-matl.blogspot.in/2013/06/resolving-properties-with-spring.html

How to read properties (or any other text) file in Java Spring MVC app?

I need to read java properties file inside my Spring MVC app but I can't find the way to do that. I tried several answers from similar question here on SO, but I was unsuccessful. I'm new to Java, and especially Spring MVC so I probably messed up something.
I'm not sure anymore that the file is being successfully deployed. I'm using Tomcat btw.
If you are using Spring 3.1+ you can use the #PropertySource annotation:
#Configuration
#PropertySource("classpath:/com/example/app.properties")
public class AppConfig {
// create beans
}
or for XML-based configuration you can use the <context:property-placeholder>:
<beans>
<context:property-placeholder location="classpath:com/example/app.properties"/>
<!-- bean declarations -->
</beans>
then you can autowire the key in the properties file using the #Value annotation:
#Value("${property.key}") String propertyValue;
Read more details in the Spring reference docs.
You can have properties files automatically loaded in Spring by using the PropertySourcesPlaceholderConfigurer.
Here is an example of configuring a PropertySourcesPlaceholderConfigurer using Spring JavaConfig:
#Bean
public static PropertySourcesPlaceholderConfigurer properties() {
PropertySourcesPlaceholderConfigurer props = new PropertySourcesPlaceholderConfigurer();
props.setLocations(new Resource[] {
new ClassPathResource("/config/myconfig.properties"),
new ClassPathResource("version.properties")
});
}
This will load the properties from the files above on the classpath.
You can use these properties in property replacements within your application. For example, assume that there is a property in one of those files above named myprop. You could inject myprop's value into a field using the following:
#Value(${myprop})
private String someProperty;
You can also access the values of the properties by injecting Spring's Environment object into your classes.
#Resource
private Environment environment;
public void doSomething() {
String myPropValue = environment.getProperty("myprop");
}
In order to read any old file from within a web application the link that Frederic posted in the comments above provides a good explanation of the normal classloader hurdles one encounters when attempting to read files from within their war file and the solutions around it.
You can try the below code.
Add this to servelt-context.xml
<context:property-placeholder location="classpath:config.properties"/>
And to access the contents of config file in java,
#Value("${KEY}")
private String value;

Load properties file in Spring depending on profile

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 inject all properties from Spring into a bean?

I want to inject a map containing all the properties that spring knows of (which are inserted by a library) to a config class that I have through the spring xml. Is that possible?
<bean class="Config">
<constructor-arg name="env">
<map>
//inject all properties?
</map>
</constructor-arg>
</bean>
Why don't you just inject the Spring Context? Through the Context, you can look up any bean via its name.
Edit:
From this answer, you could also use the following:
<bean class="Config">
<constructor-arg name="env">
<util:properties location="${path.to.properties.file}"/>
</constructor-arg>
</bean>
Where your "env" constructor argument is a java.util.Properties object.
For later versions of Spring (including spring-boot) that support the injection of an Environment you can use this to access all properties loaded.
To answer this question inject a AbstractEnvironment so that you are able to call the getPropertySources() method that will allow you to see where the properties have been loaded from (e.g. a file, OS variables, etc)
#Autowired
public Config(AbstractEnvironment environment)
{
MutablePropertySources propertySources = environment.getPropertySources();
// inspect propertySources to see all properties loaded by Spring
}
Can you not extend the library class that you use and instantiate your bean instead of the default library one? Then you would be able to inspect all the values.
Otherwise, if you know the signature of the library, you can always use AOP to weave some code around the library and get access to the properties there. A bit more complicated, but still gets you where you need to go. You can definitely use AspectJ (which requires a little more config) or even Spring AOP, depending how things are being accessed.
If you want/need more insight on this, let me know.

Categories

Resources