15 August 2014

Automatically Reload Properties File in Spring Framework

Can't believe I spent so many days trying to get this to work, in the end it is actually so simple lol, now there is no need to restart my server whenever the properties file change, spring will detect and reload it on the fly. I created a demo project in hope that it will help someone, there really is no need to spend days looking for such a simple answer ;)

My Project Structure

demo.jsp
<h1>${hostname}</h1>
<h1>${driver}</h1>
<h1>${reading}</h1>
<h1>${writing}</h1>

database.properties
hostname=localhost
driver=oracle.jdbc.OracleDriver

language.properties
reading=english,chinese,malay
writing=english,malay

web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns="http://java.sun.com/xml/ns/javaee" 
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
id="WebApp_ID" version="2.5">

  <display-name>Demo</display-name>
  
  <servlet>
<servlet-name>demo</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
<servlet-name>demo</servlet-name>
<url-pattern>/</url-pattern>
  </servlet-mapping>

</web-app>

demo-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">

    <context:component-scan base-package="demo.controller" />
    <mvc:annotation-driven />
    
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>

<!-- WebContent/ -->
<mvc:resources mapping="/**" location="/" />

</beans>

ReloadableResourceBundleMessageSource is the "magic" class that helps me to auto reload my properties.

basenames means I can specify multiple property files, as you can see I have specified database and language, here you don't need to put the full file name, just put whatever before the fullstop and spring will do the rest. You can also put basename but it means you can only point to one property file.

cacheSeconds is the number of seconds to to cache loaded properties file, put 1 second so spring will reload every one second (I guess? Anyway 1 works).

demoBeans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">

    <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
     <property name="basenames">
     <list>
     <value>classpath:properties/database</value>
     <value>classpath:properties/language</value>
     </list>
     </property>
<property name="cacheSeconds" value="1"/> 
</bean>

</beans>

Now you just need to create an ApplicationContext to grab the demoBeans.xml and then you can use this context to access the properties files, just use getMessage().

DemoController.java
package demo.controller;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class DemoController {

ApplicationContext demoBean = new ClassPathXmlApplicationContext("classpath:beans/demoBeans.xml");

@ModelAttribute("hostname")
public String getHostNameViaProperties() {
return demoBean.getMessage("hostname", null, null);
}

@ModelAttribute("driver")
public String getDriverViaProperties() {
return demoBean.getMessage("driver", null, null);
}

@ModelAttribute("reading")
public String getReadingViaProperties() {
return demoBean.getMessage("reading", null, null);
}

@ModelAttribute("writing")
public String getWritingViaProperties() {
return demoBean.getMessage("writing", null, null);
}

@RequestMapping("/demo")
public String showDemoPage(Model model) {
return "demo";
}

}

Now let's start the application server and then run the demo.

Initial Load

Now let's make some changes to the properties file.

database.properties
hostname=192.168.1.1
driver=my.car.Driver.lol

language.properties
reading=tamil,japanese,korean
writing=russian,italian

Finally let us reload the page WITHOUT restarting the application server.

Reloaded Page Without Restarting Server

Hope it helps!

No comments: