Spring context:property-placeholder can't resovle nested variables - spring

I have two projects -- project-web, and project-service, both of them use Spring core 3.1.3 and have configurations to load properties from corresponding property files:
project-web -- Spring Integration based project, in its spring config file:
<context:property-placeholder location="WEB-INF/spring-integration/spring-integration.properties" ignore-resource-not-found="true" />
<import resource="classpath*:META-INF/spring/applicationContext.xml" />
where the import is to include the spring configuration file from project-service, and in the project-service project, I have following configured:
<context:property-placeholder location="classpath:META-INF/application.properties, classpath:META-INF/db.properties" ignore-resource-not-found="true"/>
<import resource="classpath:META-INF/spring/applicationContext-data.xml"/>
where the import to include Spring configuration for the DAOs, inside the applicationContext-data.xml I have:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${db.${db.type}.driver}" />
<property name="url" value="${db.${db.type}.url}"/>
<property name="username" value="${db.${db.type}.username}" />
<property name="password" value="${db.${db.type}.password}" />
</bean>
When I run the unit tests for project-service, everything is fine, all the variables are resolved correctly without any problem. But when I run the project-web (project-service will be included as a .jar file in the WEB-INF/lib folder of project-web), it throws error during start up saying can't resolve ${db.type}:
org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'dataSource' defined in class path resource [META-INF/spring/applicationContext-data.xml]: Could not resolve placeholder 'db.type' in string value "db.${db.type}.driver"
at org.springframework.beans.factory.config.PlaceholderConfigurerSupport.doProcessProperties(PlaceholderConfigurerSupport.java:209) ~[spring-beans-3.1.3.RELEASE.jar:3.1.3.RELEASE]
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.processProperties(PropertySourcesPlaceholderConfigurer.java:174) ~[spring-context-3.1.3.RELEASE.jar:3.1.3.RELEASE]
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.postProcessBeanFactory(PropertySourcesPlaceholderConfigurer.java:151) ~[spring-context-3.1.3.RELEASE.jar:3.1.3.RELEASE]
......................
Note: I can't declare everything in the project-web, because project-service will be also used by other projects. Anyone know why in project-service works when it runs alone but not when included by the project-web? It can't resolve the nested variable ${db.type}

The problem is that your first PropertyPlaceHolderConfigurer is trying to resolve the placeholder that needs to be resolved by the second one.
You can either use a different prefix for each one (e.g. !{ instead of ${ for one of them), or set
ignore-unresolvable="true"
on the first one - then it will leave the resolution to the other one.

Related

Apache Camel Spring DSL, referring to an environment variable HOSTNAME

I am using Apache Camel 2.16.0 with Spring DSL
I have a Spring context XML in which I have defined a Property PlaceHolder to read the properties from various files as follows-
<bean id="propertyPlaceholder" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
<property name="ignoreResourceNotFound" value="false"/>
<property name="locations">
<list>
<value>classpath:/properties/versioning.properties</value>
<value>classpath:/properties/#{inetAddress.hostName}.properties</value>
</list>
</property>
</bean>
<bean id="inetAddress" class="java.net.InetAddress" factory-method="getLocalHost"/>
The property values are used to construct other beans such as -
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.gjt.mm.mysql.Driver"/>
<property name="url" value="${${LIVE_}DATASOURCE_URL}"/>
<property name="username" value="${${LIVE_}DATASOURCE_USERNAME}"/>
<property name="password" value="${${LIVE_}DATASOURCE_PASSWORD}"/>
</bean>
This works fine, I can see the beans being created.
I also have another Spring Context XML in the same app which has a camel context and I want to use the some other properties defined in the same properties files. I know that camel supports Spring Property Placeholder, see below excerpts from the context -
<camelContext id="charge-process-context" xmlns="http://camel.apache.org/schema/spring">
<propertyPlaceholder id="properties"
location="classpath:/properties/versioning.properties,
properties/${env:HOSTNAME}.properties"
xmlns="http://camel.apache.org/schema/spring" />
.....
.....
</camelContext>
As part of this context, I have a route that uses https component that uses the values from the property file such as below -
<to uri="https4:{{LIVE_AUTH_RESPONSE_HOST}}:{{LIVE_AUTH_RESPONSE_PORT}}/{{LIVE_AUTH_RESPONSE_CONTEXT_PATH}}"/>
This route does not start and throws following exception -
Caused by: java.lang.IllegalArgumentException: Cannot find system environment with key: HOSTNAME
at org.apache.camel.util.FilePathResolver.resolvePath(FilePathResolver.java:54)
at org.apache.camel.component.properties.PropertiesComponent.parseLocations(PropertiesComponent.java:434)
at org.apache.camel.component.properties.PropertiesComponent.parseUri(PropertiesComponent.java:163)
at org.apache.camel.component.properties.PropertiesComponent.parseUri(PropertiesComponent.java:148)
at org.apache.camel.impl.DefaultCamelContext.resolvePropertyPlaceholders(DefaultCamelContext.java:2261)
at org.apache.camel.model.ProcessorDefinitionHelper.resolvePropertyPlaceholders(ProcessorDefinitionHelper.java:730)
at org.apache.camel.model.ProcessorDefinition.createOutputsProcessorImpl(ProcessorDefinition.java:427)
at org.apache.camel.model.ProcessorDefinition.createOutputsProcessor(ProcessorDefinition.java:413)
at org.apache.camel.model.ProcessorDefinition.createOutputsProcessor(ProcessorDefinition.java:165)
at org.apache.camel.model.ExpressionNode.createFilterProcessor(ExpressionNode.java:109)
at org.apache.camel.model.WhenDefinition.createProcessor(WhenDefinition.java:74)
at org.apache.camel.model.WhenDefinition.createProcessor(WhenDefinition.java:32)
at org.apache.camel.model.ProcessorDefinition.createProcessor(ProcessorDefinition.java:483)
at org.apache.camel.model.ChoiceDefinition.createProcessor(ChoiceDefinition.java:135)
at org.apache.camel.model.ProcessorDefinition.makeProcessorImpl(ProcessorDefinition.java:534)
at org.apache.camel.model.ProcessorDefinition.makeProcessor(ProcessorDefinition.java:495)
at org.apache.camel.model.ProcessorDefinition.addRoutes(ProcessorDefinition.java:219)
at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:1069)
Please Note: I am deploying my application as a war file on Tomcat 8 on an AWS instance.
I have a Dev Environment on Windows 10 and I have found this working on the Windows OS. I have also seen that the file FilePathResolver.java in Apache Camel 2.16 uses System.getenv(key) to obtain the value i.e. System.getenv("HOSTNAME") which returns a null on AWS instance and a correct value on Windows 10. I also tried using env:hostname (small case letters for unix) but still no luck ...
I found a solution at http://camel.apache.org/using-propertyplaceholder.html
at Bridging Spring and Camel Property Placeholders
It mentions following -
The Spring Framework does not allow 3rd party frameworks such as Apache Camel to seamless hook into the Spring property placeholder mechanism. However you can easily bridge Spring and Camel by declaring a Spring bean with the type org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer, which is a Spring org.springframework.beans.factory.config.PropertyPlaceholderConfigurer type.

Could not resolve placeholder in string value with external files

I have configured a config.xml file choosing the appropriate properties file depending on environment. I am running this as a Spring Boot app with Apache Camel.
The config looks like this.
<bean id="properties"
class="org.apache.camel.component.properties.PropertiesComponent">
<property name="locations" ref="locations" />
</bean>
<bean id="bridgePropertyPlaceholder"
class="org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer">
<property name="locations" ref="locations" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>
<beans profile="dev">
<util:list id="locations">
<value>classpath:config/users.properties</value>
<value>classpath:config/application.properties</value>
<value>classpath:config/application-dev.properties</value>
</util:list>
<beans profile="test">
<util:list id="locations">
<value>file:${project.dir}/config/users.properties</value>
<value>file:${project.dir}/config/application.properties</value>
</util:list>
When using the test profile I want to use the external files defined in the config (because I dont want to commit username/password to repo). That seems to work okay.
However, my users.properties file contains:
username=myusername
password=mypassword
and my application.properties contains:
loginParameters=username=${username}&password=${password}
when running java -jar myjar.jar --spring.profiles.active=test I encounter:
java.lang.IllegalArgumentException: Could not resolve placeholder 'username' in string value '${username}&password=${password}'
It clearly loads the properties files because it states:
Loading properties file from URL: file:...../users.properties
Loading properties file from URL: file:...../application.properties
Bridging Camel and Spring property placeholder configurer with id: bridgePropertyPlaceholder
...
And then the exception occurs. How can I resolve the issue where the application.properties file doesn't recognize my properties defined in users.properties? Everything works okay when running the dev-profile.
in your application properties, use $simple{username} and $simple{password} to tell Camel to put the values there.

Tomcat Context-Params ignored in Spring webapp when using PropertyPlaceholder

I was previously using, the now deprecated, class org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer to load a properties file from the server's filesystem. I had the following bean definied:
<bean class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer">
<property name="locations" value="${config}"/>
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="searchContextAttributes" value="true"/>
<property name="contextOverride" value="false"/>
<property name="ignoreResourceNotFound" value="true"/>
<property name="searchSystemEnvironment" value="false"/>
</bean>
The config is an argument that is passed when starting Tomcat, i.e.
-Dconfig=/path/to/application.properties
For the webapp I also have a context file:
<Context docBase="/path/to/application.war">
<Parameter name="host" value="localhost" override="false"/>
<Parameter name="port" value="8080" override="false"/>
</Context>
If the .properties file, specified by the -Dconfig argument, contains the property that some other bean references then the value from the .properties file is used, otherwise the value from the the context xml file is used.
This allowed me to have a set of default properties deployed with the WAR and if required, I was able to specify a .properties file to override particular values.
Now, I'm updating to use the new property abstractions in Spring 3.1 but I can't seem to figure out what the equivalent approach to this is?
I have the same context file and war deployed in the same way, and I now have the following in the application:
<context:property-placeholder
location="${config}"
system-properties-mode="OVERRIDE"
ignore-resource-not-found="true"
ignore-unresolvable="true"/>
This finds and uses the properties from the properties file, BUT it does not use the values from the context XML file.
How do I get my application to use the context params when using this new property-placeholder?
Thanks.
To summarise the problem is that the Context Parameters from the servlet context file were not being used to resolve placeholders when using the new Property Placeholder namespace introduced in Spring 3.1.
I have figured out a solution, with the following
<context:property-placeholder location="${config}" local-override="true" ignore-resource-not-found="true"/>
I can specify one or more *.properties files on the local filesystem using a JVM arg, eg:
-Dconfig=/path/app.properties
If a placeholder property cannot be resolved after checking the app.properties file then the Servlet Context Parameters are checked.
This allows me to have default values using context params in a the web.xml and where I need to I can override these values by specifying the location of *.properties files using the config JVM arg.
The key to getting it to work this way was to include local-override="true", which is false by default. I'm not fully sure that it makes sense, since the description for that attribute is:
Specifies whether local properties override properties from files. Default
is "false": Properties from files override local defaults.
If the same property key exists in the app.properties and the web.xml the value from the app.properties is used.
Spring uses a default property file unless the user-based property file is defined. If you want to control .properties file, please follow the instructions posted here.
If you want to take advantage of application.properties there are two ways to do it.
<!-- allows for ${} replacement in the spring xml configuration from the
system.properties file on the classpath -->
<util:properties id="appProperties" location="classpath:application.properties"/>
<context:property-placeholder location="classpath:application.properties"/>
util tag lets you to use a Property class to read the properties across your application. For example:
#Autowired
public MyPropertyReader(Properties appProperties) {
String prop1 = appProperties.getProperty("my.address");
String prop2 = appProperties.getProperty("my.version");
}
If you want to use the values within your context file use the context:property-placeholder tag. Then you can use your values as
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory" p:brokerURL="${jms.primary.server}"/>
where for example jms.primary.server=172.168.10.18:6161 in application.properties.

Can you configure velocity to use a toolbox file from the classpath?

I'm using spring with a VelocityViewResolver configured by Spring
<bean id="velocityViewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
<property name="order" value="1" />
<property name="suffix" value=".vm" />
<property name="contentType" value="text/html;charset=UTF-8" />
<property name="exposeSpringMacroHelpers" value="true" />
<property name="toolboxConfigLocation" value="/WEB-INF/velocityToolbox.xml" />
</bean>
This works if the velocityToolbox.xml file is under WEB-INF ; however I'm trying to put it in the classpath. Is there a way to do it ?
I've tried specifying toolboxConfigLocation as "classpath:/wherever/velocityToolbox.xml", but it does not find the resource, and I end up with no toolbox configured, and a NPE at runtime (for some reason, it seems like the code expect the location to start with a '/', or add the '/' itself before looking for the resource).
Given that the resource is located using ServletContext.getResourceAsStream, with the content of the toolboxConfigLocation property prefixed by a "/", is there a way I can "define" a resource in my spring config that would somehow 'point' to an actuall classpath resource ?
Any idea is welcome.
I believe it has to be a full path, eg:
/WEB-INF/classes/com/myapp/resources/toolbox.xml
The ServletToolboxManager#getInstance(ServletContext context, String toolboxConfigLocation) method is used to generate a ToolboxManager configured with the toolbox in order to create the ChainedContext used in the VelocityToolboxView.
This method pre-appends the path with a '/' if one does not exist, and then uses ServletContext#getResourceAsStream(String path) to read it in.
With this in mind, you'll have success if you set it as a full path from the context root.

Spring alias not available when running in Grails

I have a problem with Spring aliases in Grails. I have a library .jar file containing classes and Spring configuration that is not working as expected. It does work as expected when I import them from a standard (no Grails) Java app.
The current configuration contains this.
<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="contextPath" value="uk.co.company.package"/>
</bean>
<alias name="marshaller" alias="unmarshaller"/>
And fails with an error.
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'unmarshaller' is defined
Changing the configuration to the following then leads it to work as expected.
<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="contextPath" value="uk.co.company.package"/>
</bean>
<bean id="unmarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="contextPath" value="uk.co.company.package"/>
</bean>
The configuration is being imported and is being read. For some reason the alias is unavailable when I try to use it. What is that reason?
This is with Grails 1.3.7 and Spring 3.0.5.
I am seeing this issue as well. You can get around it by defining the alias in the Grails resources.xml or in my case in my plugin doWithSpring closure:
springConfig.addAlias "aliasName", "beanName"
I'd expect the importBeans to import alias as well
This link might be helpful for you:
http://burtbeckwith.com/blog/?p=85
It is mentioned there that aliases don't work at least when declared in the resources.xml. The post also mentions a way of declaring aliases programatically. But it seems like this post was written a while back and not sure how relevant it is with grails 1.3.7.

Resources