Load configuration files dynamically using Spring - spring

We already have a setup in which we are loading files like :
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>file:${AN_ENV_CONFIGURED_DIR}/project.properties</value>
</list>
</property>
The config files are there in server in a separate location. I want to load the configuration files dynamically if there is a change in it. Now since I already have this setup is there a simple way to reload config files from Spring other than using TimerTask and if its the only way then still its not sufficing the need to loading the file immediately.

You can use ReloadableResourceBundleMessageSource, following is the code snippet.
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<value>file:${AN_ENV_CONFIGURED_DIR}/project.properties</value>
</list>
</property>
<property name="cacheSeconds" value="1"/>
</bean>
For more info : http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/support/ReloadableResourceBundleMessageSource.html

Use the below bean to reload config.properties every 1 second.
#Component
public class PropertyLoader {
#Autowired
private StandardEnvironment environment;
#Scheduled(fixedRate=1000)
public void reload() throws IOException {
MutablePropertySources propertySources = environment.getPropertySources();
PropertySource<?> resourcePropertySource = propertySources.get("class path resource [config.properties]");
Properties properties = new Properties();
InputStream inputStream = getClass().getResourceAsStream("/config.properties");
properties.load(inputStream);
inputStream.close();
propertySources.replace("class path resource [config.properties]", new PropertiesPropertySource("class path resource [config.properties]", properties));
}
}
Your main config will look something like:
#EnableScheduling
#PropertySource("classpath:/config.properties")
public class HelloWorldConfig {
}
Then access the property any where:
#Autowired
private Environment environment;
environment.get("my.property");

Related

Spring boot - 2 JNDI configuration

Posted my current configuration
#Configuration
public class Config {
#Value("${spring.datasource.primary.jndi-name}")
private String primaryJndiName;
#Value("${spring.datasource.secondary.jndi-name}")
private String secondaryJndiName;
#Primary
#Bean(destroyMethod = "") // destroy method is disabled for Weblogic update app ability
public DataSource primaryDs() {
JndiDataSourceLookup lookup = new JndiDataSourceLookup();
return lookup.getDataSource(primaryJndiName);
}
#Bean(destroyMethod = "") // destroy method is disabled for Weblogic update app ability
public DataSource secondaryDs() {
JndiDataSourceLookup lookup = new JndiDataSourceLookup();
return lookup.getDataSource(secondaryJndiName);
}
}
I implemented this way and it is working
you can put your jndi values in one properties file and then load that property file in your bean defination.xml
jndi.properties
#JNDI property for job repository
job.repository.db.connection=jdbc/pgDB
#JNDI property for application
application.db.connection=jdbc/db2Conn
Bean-defination.xml
<bean id="propertyPlaceholderConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath*:/properties/jndi.properties</value>
</list>
</property>
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
</bean>
<bean id="jobRepoDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="${job.repository.db.connection}" />
</bean>
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="${application.db.connection}" />
</bean>

Reading message from properties file in spring4

I am facing an issue in reading the properties file in spring mvc4. To read messages I have added following in spring-servlet.xml file located under WEB-INF folder.
<context:component-scan base-package="com.test.restful.producer" />
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="location">
<value>classpath:application.properties</value>
</property>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
In my controller class,
#Value("${MSG}")
private String msg;
i am getting msg as null. Please helpout me how to load properties file. My appilcation.properties file is available in classpath only.
I am using Spring-4.0.5
Thank you
You can try this xml for creating properties bean.
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="classpath:propertyFile.properties" name="propertiesBean"/>
Or go for non xml version as below
#PropertySource("classpath:propertyFile.properties")
public class AppConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}

How to log properties loaded by Spring?

is it possible to simply log all the content of the properties file loaded by spring with <context:property-placeholder /> ?
Thanks
You can set the log level of org.springframework.core.env.PropertySourcesPropertyResolver to "debug". Then, you will be able to see the value of the properties during resolving.
You can do it this way:
<context:property-placeholder properties-ref="myProperties"/>
<bean id="myProperties"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations">
<list>
.. locations
</list>
</property>
</bean>
and add a logging bean similar to one below (here annotation based and with slf4j api):
#Component
public class PropertiesLogger {
private static Logger logger = LoggerFactory.getLogger(PropertiesLogger.class);
#Resource("myProperties")
private Properties props;
#PostConstruct
public void init() {
for (Map.Entry<Object, Object> prop : props.entrySet()) {
logger.debug("{}={}", prop.getKey(), prop.getValue());
}
}
}

Reading a list from a file .properties using Spring properties place holder

I want to fill a bean list property using Spring properties place holder.
Context file
<bean name="XXX" class="XX.YY.Z">
<property name="urlList">
<value>${prop.list}</value>
</property>
</bean>
Properties File
prop.list.one=foo
prop.list.two=bar
Any help would be much appreciated
Use a util:properties element to load your properties. You can use PropertyPlaceholderConfigurer to specify the path to your file:
<bean name="XXX" class="XX.YY.Z">
<property name="urlList">
<util:properties location="${path.to.properties.file}"/>
</property>
</bean>
Update I've misunderstood the question; you only want to return properties where key starts with specific string. The easiest way to achieve that would be to do so within setter method of your bean. You'll have to pass the string to your bean as a separate property. Extending the above declaration:
<bean name="XXX" class="XX.YY.Z" init-method="init">
<property name="propertiesHolder">
<!-- not sure if location has to be customizable here; set it directly if needed -->
<util:properties location="${path.to.properties.file}"/>
</property>
<property name="propertyFilter" value="${property.filter}" />
</bean>
In your XX.YY.Z bean:
private String propertyFilter;
private Properties propertiesHolder;
private List<String> urlList;
// add setter methods for propertyFilter / propertiesHolder
// initialization callback
public void init() {
urlList = new ArrayList<String>();
for (Enumeration en = this.propertiesHolder.keys(); en.hasMoreElements(); ) {
String key = (String) en.nextElement();
if (key.startsWith(this.propertyFilter + ".") { // or whatever condition you want to check
this.urlList.add(this.propertiesHolder.getProperty(key));
}
} // for
}
If you need to do this in many different places you can wrap the above functionality into a FactoryBean.
A simpler solution:
class Z {
private List<String> urlList;
// add setters and getters
}
your bean definition
<bean name="XXX" class="XX.YY.Z">
<property name="urlList" value="#{'${prop.list}'.split(',')}"/>
</bean>
Then in your property file:
prop.list=a,b,c,d
<bean id="cpaContextSource" class="org.springframework.ldap.core.support.LdapContextSource">
<property name="urls">
<bean class="org.springframework.util.CollectionUtils" factory-method="arrayToList">
<constructor-arg type="java.lang.Object">
<bean class="org.springframework.util.StringUtils" factory-method="tokenizeToStringArray">
<constructor-arg type="java.lang.String" value="${myList}"/>
<constructor-arg type="java.lang.String" value=" "/>
</bean>
</constructor-arg>
</bean>
</property>
where:
myList=http://aaa http://bbb http://ccc
The only way i see here is, implement the interface 'MessageSourceAware' to get the messageResource, and then manually populate your list.
class MyMessageSourceAwareClass implemets MessageSourceAware{
public static MessageSource messageSource = null;
public void setMessageSource(MessageSource _messageSource) {
messageSource = _messageSource;
}
public static String getMessage( String code){
return messageSource.getMessage(code, null, null );
}
}
--- Properties File ---
prop.list=foo;bar;one more
Populate your list like this
String strlist = MyMessageSourceAwareClass.getMessage ( "prop.list" );
if ( StringUtilities.isNotEmptyString ( strlist ) ){
String[] arrStr = strList.split(";");
myBean.setList ( Arrays.asList ( arrStr ) );
}
Just add the following Bean definition
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:myprops.properties</value>
</list>
</property>
</bean>
To use it like so please note port is defined in myprops.properties
<bean id="mybean" class="com.mycompany.Class" init-method="start">
<property name="portNumber" value="${port}"/>
</bean>
There are several ways , one of them is below.
XmlBeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml"));
PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer();
cfg.setLocation(new FileSystemResource("jdbc.properties"));
cfg.postProcessBeanFactory(factory);

Sharing Spring Security Configuration Between Applications

I'm brand spanking new at Spring and have gotten a majority of the knowledge I do have from the Spring Recipes book from Apress.
I've got LDAP authentication working with Spring Security within one webapp. I would like to rip out my application context beans and properties files from this one webapp, however, and somehow externalize them so that all of our webapps can reference the same beans. So when we need to change something (like the ldapuser or the ldap urls), we change it in one place and the rest of the apps just know.
UPDATE
I've implemented Reloadable Spring Properties which is reloading properties when the files they come from are touched. I am using encrypted properties, however, so below is class I created on top of the Reloadable Spring Properties ones.
ReloadingEncryptablePropertyPlaceholderConfigurer.java
package;
import java.util.Properties;
import java.util.Set;
import org.apache.commons.lang.Validate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasypt.encryption.StringEncryptor;
import org.jasypt.util.text.TextEncryptor;
import org.jasypt.properties.PropertyValueEncryptionUtils;
import org.springframework.beans.factory.BeanDefinitionStoreException;
public class ReloadingEncryptablePropertyPlaceholderConfigurer extends ReloadingPropertyPlaceholderConfigurer {
protected final Log logger = LogFactory.getLog(getClass());
private final StringEncryptor stringEncryptor;
private final TextEncryptor textEncryptor;
public ReloadingEncryptablePropertyPlaceholderConfigurer(TextEncryptor textEncryptor) {
super();
logger.info("Creating configurer with TextEncryptor");
Validate.notNull(textEncryptor, "Encryptor cannot be null");
this.stringEncryptor = null;
this.textEncryptor = textEncryptor;
}
public ReloadingEncryptablePropertyPlaceholderConfigurer(StringEncryptor stringEncryptor) {
super();
logger.info("Creating configurer with StringEncryptor");
Validate.notNull(stringEncryptor, "Encryptor cannot be null");
this.stringEncryptor = stringEncryptor;
this.textEncryptor = null;
}
#Override
protected String convertPropertyValue(String originalValue) {
if (!PropertyValueEncryptionUtils.isEncryptedValue(originalValue)) {
return originalValue;
}
if (this.stringEncryptor != null) {
return PropertyValueEncryptionUtils.decrypt(originalValue, this.stringEncryptor);
}
return PropertyValueEncryptionUtils.decrypt(originalValue, this.textEncryptor);
}
#Override
protected String parseStringValue(String strVal, Properties props, Set visitedPlaceholders) throws BeanDefinitionStoreException {
return convertPropertyValue(super.parseStringValue(strVal, props, visitedPlaceholders));
}
}
And here's how I use it in my securityContext.xml:
<bean id="securityContextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<constructor-arg value="ldaps://ldapserver" />
<property name="urls" value="#{ldap.urls}" />
</bean>
<bean id="timer" class="org.springframework.scheduling.timer.TimerFactoryBean">
<property name="scheduledTimerTasks">
<bean id="reloadProperties" class="org.springframework.scheduling.timer.ScheduledTimerTask">
<property name="period" value="1000"/>
<property name="runnable">
<bean class="ReloadConfiguration">
<property name="reconfigurableBeans">
<list>
<ref bean="configproperties"/>
</list>
</property>
</bean>
</property>
</bean>
</property>
</bean>
<bean id="configproperties" class="ReloadablePropertiesFactoryBean">
<property name="location" value="classpath:ldap.properties"/>
</bean>
<bean id="ldapPropertyConfigurer" class="ReloadingEncryptablePropertyPlaceholderConfigurer">
<constructor-arg ref="configurationEncryptor" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="properties" ref="configproperties"/>
</bean>
<bean id="jasyptConfig" class="org.jasypt.encryption.pbe.config.SimpleStringPBEConfig">
<property name="algorithm" value="PBEWithMD5AndTripleDES" />
<property name="password" value="########" />
</bean>
<bean id="configurationEncryptor" class="org.jasypt.encryption.pbe.StandardPBEStringEncryptor">
<property name="config" ref="jasyptConfig" />
</bean>
How about:
Writing a method that returns a list
of LDAP servers - reading from a
database table or property files
expose this wethod via jndi and use it to inject a list of the servers into your spring config
If you need the ldap servers to be refreshed dynamically you could have a job poll for changes periodically or else have an admin webpage or jmx bean to trigger the update. Be careful of concurrency isses for both these methods (something reading the list while you are updating)
Wouldn't that be Spring Security? It can deal with LDAPs. And if you make it one security service that everyone uses, wouldn't that be the way to manage it?

Resources