Nested properties across property place holder configurers - java

I have multiple property files, a general one called application.properties and a more specific on called [HostName].properties
The Hostname.properties is set to higher priority than application.properties.
In application.properties I set the db.url:
db.url=jdbc:mysql://${db.host}:3306/${db.schema}?autoReconnect=true&sessionVariables=storage_engine=InnoDB;
I also set the db.host and db.schema values in this properties file.
db.schema=default
db.host=localhost
Then in [Hostname].properties I want to override the values for db.host and db.schema:
db.schema=specific
db.host=specific_hostname
However, it seems as if this nesting is not working.
The db.schema value resolves to "specific" but the db.url property resolves to the db.schema specified in the same file as the db.url is defined in.
Consider the following test snippet:
#Value("${db.url}")
private String dbUrl;
#Value("${db.schema}")
private String dbSchema;
#Test
public void testUrlTest() {
System.out.println(dbUrl);
System.out.println(dbSchema);
}
First value printed out is
jdbc:mysql://localhost:3306/default?autoReconnect=true&sessionVariables=storage_engine=InnoDB;
Second value is:
specific
Is this by design? I would love to be able to override nesting from one property configurer to the next.
Is there some way around this?
UPDATE:
The Property place holders are configured in the spring config xml as follows:
<context:property-placeholder order="10"
location="classpath:xxx.yyy/application.properties"
ignore-unresolvable="true" ignore-resource-not-found="false" />
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" ref="hostnameLookup" />
<property name="ignoreResourceNotFound" value="true" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="order" value="5" />
</bean>
<bean class="xxx.yyy.util.HostNameLookupFactory" id="hostnameLookup"
autowire-candidate="false">
<property name="prefix" value="classpath:xxx.yyy/" />
<property name="suffix" value=".properties" />
</bean>
The HostNameLookupFactory is a factory bean that provides the local host name.

Related

org.springframework.beans.NotWritablePropertyException. Invalid property <blah> is not writable or has an invalid setter method

I have the requirement to remove all passwords and encryption keys from the source code of my project. I'm struggling to get this to work in my spring-servlet.xml file.
This worked before I tried making the change:
the database username, encrypted password, URL and driver were defined in a file jdbc_server.properties as a classpath resource.
the encryption/decryption key was passed on start-up as -DENCRYPTION_PASSWORD=
I want to move the jdbc_server_properties file to the filesystem and include the key in the file.
This is my last (of at least 25) attempt at getting this to work.
<bean id="propertyConfigurer" class="org.jasypt.spring4.properties.EncryptablePropertyPlaceholderConfigurer">
<constructor-arg ref="standardEncryptor" />
<property name="jdbcUsername" value="JDBC_USERNAME" />
<property name="jdbcPassword" value="JDBC_PASSWORD" />
<property name="jdbcUrl" value="JDBC_URL" />
<property name="jdbcDriver" value="JDBC_DRIVER" />
<property name="locations">
<list>
<value>file:///${USERPROFILE}/credentials/jdbc_server.properties</value>
</list>
</property>
</bean>
<bean id="standardEncryptor" class="org.jasypt.encryption.pbe.StandardPBEStringEncryptor">
<property name="config" ref="environmentVariablesConfiguration" />
</bean>
<bean id="environmentVariablesConfiguration" class="org.jasypt.encryption.pbe.config.EnvironmentStringPBEConfig">
<property name="algorithm" value="PBEWithMD5AndDES" />
<property name="passwordSysPropertyName" value="ENCRYPTION_PASSWORD" />
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close" p:driverClassName="${jdbcDriver}" p:url="${jdbcUrl}"
p:username="${jdbcUsername}" p:password="${jdbcPassword}">
</bean>
Using the above configuration, I get a NotWritablePropertyException exception. I've seen tons of posts on this issue but not where both the properties and encryption key are in a file on the filesystem.
When this was working and the properties were read from a file in the classpath, there were no getters/setters for jdbcUsername (or the other properties) so I don't know why it's failing in this way now.
I tried adding the getters and setters (as String) to my BaseDaoImpl class but I still get the same error so if I'm supposed to add them, I'm not sure where they go.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'propertyConfigurer' defined in ServletContext resource [/WEB-INF/spring-servlet.xml]: Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'jdbcUsername' of bean class [org.jasypt.spring4.properties.EncryptablePropertyPlaceholderConfigurer]: Bean property 'jdbcUsername' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1568)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1276)
at ...
That is probably because jdbcUsername is not a property of EncryptablePropertyPlaceholderConfigurer.
Please, try something like this (just remove the jdbc* properties from the EncryptablePropertyPlaceholderConfigurer configuration, and use it directly in your dataSource bean):
<bean id="propertyConfigurer" class="org.jasypt.spring4.properties.EncryptablePropertyPlaceholderConfigurer">
<constructor-arg ref="standardEncryptor" />
<property name="locations">
<list>
<value>file:///${USERPROFILE}/credentials/jdbc_server.properties</value>
</list>
</property>
</bean>
<bean id="standardEncryptor" class="org.jasypt.encryption.pbe.StandardPBEStringEncryptor">
<property name="config" ref="environmentVariablesConfiguration" />
</bean>
<bean id="environmentVariablesConfiguration" class="org.jasypt.encryption.pbe.config.EnvironmentStringPBEConfig">
<property name="algorithm" value="PBEWithMD5AndDES" />
<property name="passwordSysPropertyName" value="ENCRYPTION_PASSWORD" />
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close" p:driverClassName="${JDBC_DRIVER}" p:url="${JDBC_URL}"
p:username="${JDBC_USERNAME}" p:password="${JDBC_PASSWORD}">
</bean>
Assuming your jdbc_server.properties file contains the required information:
JDBC_USERNAME=ENC(...)
JDBC_PASSWORD=ENC(...)
JDBC_URL=ENC(...)
JDBC_DRIVER=your.jdbc.driver

Spring config properties from database and properties

I asked a similar question, but based on the responses, I did a bad job describing what I am after. I have a spring 4 webapp that loads properties from a properties file. We consume those properties both via the "${proper.name"} expressions in spring, as well as by injecting a properties object into some of our classes.
We want to move most of the properties to a database table and make them reloadable. However, a few need to stay in local properties, potentially overriding the database setting. These should also be loaded dynamically after the app is running.
I know that once a particular bean is injected, it won't get reloaded, that doesn't concern me, it's up to that module to handle that. But I am having trouble getting the behavior I want. In particular, I have implemented an AbstractConfiguration from apache commons configuration to get the dual source and overriding I am after. But while it works for injecting the properties object, expressions loaded with "${prop.name}" don't work at all.
How can I get them to work? Did I override the wrong thing? Is it just some config detail?
<bean id="sysProperties" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="databaseConfigurator" />
<property name="targetMethod" value="getProperties"/>
</bean>
<bean id="databaseConfigurator" class="my.util.config.MyDatabaseConfigurator">
<property name="datasource" ref="dataSource" />
<property name="propertyFile" value="/WEB-INF/my.properties" />
<property name="applicationName" value="ThisApp" />
</bean>
<bean id="dbConfigFactory" class="org.apache.commons.configuration.ConfigurationConverter" factory-method="getProperties">
<constructor-arg ref="databaseConfigurator" />
</bean>
I haven't tested this, but I think it might work.
<bean id="sysProperties" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="databaseConfigurator" />
<property name="targetMethod" value="getProperties"/>
</bean>
<bean id="databaseConfigurator" class="my.util.config.MyDatabaseConfigurator">
<property name="datasource" ref="dataSource" />
<property name="propertyFile" value="/WEB-INF/my.properties" />
<property name="applicationName" value="ThisApp" />
</bean>
<bean name="PropertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties" ref="CommonsConfigurationFactoryBean"/>
</bean>
<bean name="CommonsConfigurationFactoryBean" class="org.springmodules.commons.configuration.CommonsConfigurationFactoryBean">
<constructor-arg ref="databaseConfigurator"/>
</bean>

How to use DatabaseConfiguration from apache commons with a properties file configures datasource name

I am moving our config properties from a properties file to a database using apache commons DatabaseConfiguration. Works fine. However, it obviously needs database definition, and I want to load that with a jndi datasource name that is itself a property. So I want to keep a property file with that property and one or two others, that would load first and get used by the database configuration.
I have tried about a million permutations and I can't seem to get it to work. If I hardcode the jndi name in the spring config, it works, but whether I include both in the same property configurator or separate ones, whether I use the order property or not...nothing seems to work.
Here's one of my iterations that didn't work:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="/WEB-INF/esp.properties" />
<property name="order" value="10" />
</bean>
<bean id="propertyConfigurer2" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="order" value="20" />
<property name="properties" ref="dbConfigFactory" />
</bean>
dbConfigFactory there is the factory that includes the databaseconfiguration bean. Does anyone know how to do this?

Spring: Loading PropertyPlaceholderConfigurer

Here is the situation:
The xml configuration looks as follows:
<bean id="ppConfig1"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>file:c:\test\env.properties</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="true" />
</bean>
<bean id="string" class="java.lang.String" depends-on="ppConfig1">
<constructor-arg value="${env.app.type}"/>
</bean>
<bean id="ppConfig2"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" depends-on="string">
<property name="locations">
<list>
<value>file:c:\test\#{string}.properties</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="false" />
</bean>
As is evident, I want load a specific property file (e.g. app1.properties) based on the value of key env.app.type in C:\test\env.properties.
The code to load/test the above config looks as follows:
ApplicationContext context = new ClassPathXmlApplicationContext(
"SpringBeans.xml");
String ss = (String)context.getBean("string");
System.out.println(ss);
This seems not working. It failing with following error:
Exception in thread "main"
org.springframework.beans.factory.BeanInitializationException: Could
not load properties; nested exception is
java.io.FileNotFoundException: C:\test\${env.app.type}.properties (The
system cannot find the file specified) at
org.springframework.beans.factory.config.PropertyResourceConfigurer.postProcessBeanFactory(PropertyResourceConfigurer.java:87)
The file c:\test\env.properties looks as follows
env.app.type=app1
One interesting observation is that when I comment out the ppConfig2, everything works fine and correct value of env.app.type is printed. I am open to any other suggestion to get around this. My basic requirement is to select the property file at run time based on the property specified in env.properties. (I am using spring 3.1.0)
You need something like this:
<context:component-scan base-package="com.foo.bar" />
<context:property-placeholder location="file:c:/test/${env.app.type}.properties" />
<bean id="service" class="com.foo.bar.ExampleService">
<property name="foo" value="${foo}" />
</bean>
package com.foo.bar;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
#Configuration
#PropertySource("file:c:/test/env.properties")
public class SpringConfig{
}
And "foo" value comes from app1.properties file. Basically, one file is being loaded with #PropertySource, the other with the regular property placeholder.
I think you are trying to do things that Spring does not like. The normal order for bean initialization is :
fully construct bean post processors
construct other beans
init other beans
As long as those 3 passes are cleanly separated, all will be ok, but you have a post processor bean (ppConfig2) depending on a simple bean (string).
IMHO, you should avoid such construct if possible. Even if it works now, you are under the risk of problem arising at any modification of you application context because you are allready on the edge.
Could it be acceptable to use environment variables or system properties to avoid that a bean post-processor depends on another bean post-processor ? If yes, you would'nt be bothered by ppConfig1 and simply have :
<bean id="ppConfig2"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" depends-on="string">
<property name="locations">
<list>
<value>file:c:\test\${configured_env}.properties</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="false" />
</bean>
If environment variables or system properties are not an option, you could use programmatic configuration as suggested by Andrei Stefan.
You can do this with util:properties:
<util:properties id="envProperties" location="env.properties"/>
<util:properties id="properties" location="#{envProperties.getProperty('env.app.type')}.properties"/>
<context:property-placeholder properties-ref="envProperties" ignore-unresolvable="true" ignore-resource-not-found="true"/>
<context:property-placeholder properties-ref="properties" ignore-unresolvable="true"/>
BTW there's a bug in Spring that prevents Spring EL from being evaluated inside location attribute in context:property-placeholder.

How to access data in another spring bean using current spring bean

I am new to Spring and I have a small requirement.
<bean name="triangle" class="com.thomson.learn.spring.triangle">
<property name="name" ref="dataSource" />
</bean>
<bean id="dataSource" class="java.lang.String">
<property name="name" value="easy" />
</bean>
I have written beans as shown above. I need to access the value easy from the bean with id="triangle", but when I try doing this I get a exception. Can someone please suggest how to overcome this problem?
Try the following:
<bean name="triangle" class="com.thomson.learn.spring.triangle">
<property name="name" ref="dataSource" />
</bean>
<bean id="dataSource" class="com.thomson.learn.spring.shape">
<property name="name" value="easy" />
</bean>
where com.thomson.learn.spring.triangle and com.thomson.learn.spring.shape must exist as classes in your project. your String class bean couldn't have worked because String has no name property (getName() method getter) to access.
If it is going to be String(s) that you want to inject into all of your beans you can refer properties from a property file and use <context:property-placeholder location="location to .properties"/>
or you can define properties in the configuration xml with <util:properties />
Example for property-placeholder
<bean id="appDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" >
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/> // Refered from property file
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="classpath:jdbc.properties"/>
A String does not have a property 'name', so you can't instantiate one the way you are trying to. Moreover, you don't need to, as you can use it directly as the value, e.g.:
<bean name="triangle" class="com.thomson.learn.spring.triangle">
<property name="name" value="easy" />
</bean>
Edit in reply to your comment:
If "name" is of type shape, then you need to inject a shape here...
<bean name="triangle" class="com.thomson.learn.spring.triangle">
<property name="name" ref="shape" />
</bean>
<bean name="shape" class="com.thomson.learn.spring.shape">
<!-- configure properties here -->
</bean>
The objects wired by Spring are supposed to be JavaBeans. What this means in practice is that they have a no-argument constructor and a set of properties, each of which has both a getter and a setter. When you write a definition such as above, Spring uses the setters to set properties you defined in the configuration file.
For instance if Shape was a class as such:
package com.thomson.learn.spring.Shape;
class Shape {
private String name;
public Shape() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Then you could wire it with the following:
<bean id="myShape" class="com.thomson.learn.spring.Shape">
<property name="name" value="blah" />
</bean>
Which really translates to Spring executing the following at runtime:
Shape myShape = new Shape();
shape.setName("blah");
That's all there is to it, except that properties can also be more complex instances of other classes of course, which is why you can use ref to refer to another bean for a property.
P.S.: Spring can use reflection to inject values in many cases, so it doesn't actually need a setter, but most people implement their beans this way for various reasons.

Categories

Resources