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
Related
the difference between ContextLoader and ContextLoaderListenerI am not understanding the difference. I have tried to search on google but I am not able to search. Please help me on this.
Performs the actual initialization work for the root application context. Called by ContextLoaderListener and ContextLoaderServlet.
Regards a "contextClass" parameter at the web.xml context-param level, falling back to the default context class (XmlWebApplicationContext) if not found. With the default ContextLoader, a context class needs to implement ConfigurableWebApplicationContext.
Passes a "contextConfigLocation" context-param to the context instance, parsing it into potentially multiple file paths which can be separated by any number of commas and spaces, like "applicationContext1.xml, applicationContext2.xml". If not explicitly specified, the context implementation is supposed to use a default location (with XmlWebApplicationContext: `
Note: In case of multiple config locations, later bean definitions
will override ones defined in earlier loaded files, at least when
using one of Spring's default ApplicationContext implementations. This
can be leveraged to deliberately override certain bean definitions via
an extra XML file.
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/context/ContextLoader.html
I have multiple datasource beans defined in my configuration file.I have 2 beans named customerDataSource. I want one of them to be instantiated only if other bean is not loaded.I was trying to use #ConditionalOnMissingBean. However it looks like this will work only when another bean of same "type" is not present. Is there a way to control this by using "name"?
The annotation has the parameter name, where you can specify the bean name that should be checked:
#ConditionalOnMissingBean(name = "beanNameToCheck")
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.
The project I am working at the moment uses camel as the routing framework.
When configuring camel context in spring we pass a property file that contains a bunch of global properties needed when configuring camel routes or for controlling run time behavior:
<camel:camelContext xmlns="http://camel.apache.org/schema/spring" id="my-id">
<camel:propertyPlaceholder location="my-system.properties" id="global-properties"/>
...
</camel:camelContext>
and say my-system.properties has an entry like below:
my-system.properties
# Global properties that control my-system configuration and run time
...
foo={{bar}}
...
When configuring the routes I can access foo property using the {{foo}} notation. It is also available to other beans using #PropertyInject annotation. However there is one use case in my design when a plain POJO not created by spring (an enum instead but this is not relevant) needs to access my foo property. Because this POJO it is passed the CamelContext as a method argument I find it natural to think I should be able to get the value of foo from there. However I spent a bit of time and could not figure out by myself how.
I know I can load the properties file again or even get the system property System.getProperty("bar") and everything will work but it looks like cheating to me.
There is an api on CamelContext to resolve property placeholders - its the resolvePropertyPlaceholders method:
http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/CamelContext.html#resolvePropertyPlaceholders(java.lang.String)
If your POJO is not being managed by the SpringContext I don't see any way you can automatically inject the property. Although your approach may not seem the most fancy or elegant, it has the advantage of not giving you any overhead you could have by using another injection tool.
I have a BeanDefinitionDecorator that makes modifications to properties that a user would set on a bean. It works fine; except if the bean is using placeholders. I am trying to find a strategy to modify those values while still have access to the original value at runtime. An example of what this would look like in XML:
<bean id="bean">
<property name="jdbcUrl" value="${jdbc.url}" />
<d:spyDecorator />
</bean>
I know that user would be writing the jdbcUrl property as "jdbc:myDatabase". What I want to do is change their property to "jdbc:spy:myDatabase". This is easy if they are just using string literals for the property value, but if they are using property placeholders I am not sure how to change the value -- because I need the original value in order to supply the new value. They key is to keep the property rewriting transparent to the user.
Are there any possible solutions for this?
I think your namespace handler can register a BeanFactoryPostProcessor (implementing Orderer with order = Integer.MAX_VALUE to be the last post processor applied). Then your BeanDefinitionDecorator will register the beans being decorated for processing with that post processor (implement it in the post processor somehow), and post processor will apply the actual property modification to that beans.
You can use PropertyPlaceholderConfigurer to substitute property values for placeholders in bean properties, aliases, and other places. Note that the replacements happen AFTER the bean definitions have been loaded, so this mechanism does not apply to <import> elements
For example:
...
<bean id="ppc"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:build.properties</value>
<value>classpath:default-emmet-substitution.properties</value>
<value>classpath:default-danno-substitution.properties</value>
<value>classpath:default-dannotate-substitution.properties</value>
<value>classpath:substitution.properties</value>
</list>
</property>
</bean>
...
For more information refer to this section of the Spring Framework docs.
EDIT - I guess from your comment you are already familiar with how placeholder replacement works, and are using PropertyPlaceholderConfigurer to do the replacements. So now you need to choose between these strategies, or some combination:
Do the placeholder replacements yourself in your custom BeanDefinitionDecorator. That would work, though you'd be duplicating a lot of code.
Have the custom BeanDefinitionDecorator modify the placeholder names to different ones that will pull in different values; e.g. "${jdbc.url}" becomes "${spy.jdbc.url}".
Extend the PropertyPlaceholderConfigurer class to modify the substituted property values; i.e. override convertProperty or convertProperties. That has the potential problem that all placeholders will get the modified values ... not just the ones in beans that you have decorated.
Create a new PropertyResourceConfigurer class to substitute different property values depending on the context. Essentially, the processProperties needs to work like the method does in a PropertyPlaceholderConfigurer, but do something different if it sees bean properties or whatever that tell it to do the "spy" substitution.
A combination of 2) and 3) looks the most promising.