7

Spring has the nice mechanism PropertyPlaceholderConfigurer for injecting values like timeouts, JDBC Urls and so forth into Spring beans for configuration purposes. Is there a sensible way to handle configuration values that can change at runtime?

UPDATE: With Spring 3.1 there is a nice way to include non-static configuration sources such as the database via PropertySources. Some ApplicationContexts provide a refresh mechanism that is in principle able to handle changing configuration values. However it stops the application first, then creates all beans fresh and then starts the application context again. However, for our purposes I would need a way to do this transparently, such that the server correctly handles currently running requests.

Another idea to do this would be a custom Scope that creates fresh objects when the configuration changes. Unfortunately the ObjectFactory provided to the Scope uses a preprocessed bean definition, such that the placeholders are not read anew from the configuration. Thus the created objects have the same configuration. :-(

Dr. Hans-Peter Störr
  • 25,298
  • 30
  • 102
  • 139
  • Probably a way to do it would be to use a PropertyOverrideConfigurer http://stackoverflow.com/a/595201/21499 , but I think the property override mechanism is rather awkward to use and error prone. – Dr. Hans-Peter Störr Apr 05 '12 at 07:52

4 Answers4

2

The following is a little odd but works. You create a custom scope named reconfigurable that throws away all beans created in that scope whenever a configuration update occurs. Thus, a fresh bean will be created after a configuration change.

The actual configuration values have to be retrieved via spring expression language since the values for both the normal ${} syntax nor PropertyOverrideConfigurer seem to be permanently fixed in the BeanDefinition. A bean declaration for a bean with a re-configurable property someProperty looks like this:

<bean class="blablu.Testbean" scope="reconfigurable"
  p:someProperty="#{ config['configexplicit']}">
  <aop:scoped-proxy />
</bean>

You need to use aop:scoped-proxy such that beans that use this bean always retrieve the freshest configured bean from the custom scope.

Declaring properties with @Value also works; if you use component scan you need to declare the scope with the annotation

@Scope(value="reconfigurableScope", proxyMode=ScopedProxyMode.TARGET_CLASS)

If you care for details: the basic idea of the scope is:

public class ReconfigurableScope implements Scope {

    private final Map<String, Object> nameToObjectMap = new ConcurrentHashMap<String, Object>();

    public Object get(final String name, final ObjectFactory<?> objectFactory) {
        Object bean = nameToObjectMap.get(name);
        if (null == bean) {
            bean = objectFactory.getObject();
            nameToObjectMap.put(name, bean);
        }
        return bean;
    }

    // called from outside on each configuration change
    public void update(final ConfigurationObservable observable, final Object arg) {
        nameToObjectMap.clear();
    }

}

Plus some thread safety and cleanup stuff: the removed beans need to be destroyed a little later and on application context close.

Dr. Hans-Peter Störr
  • 25,298
  • 30
  • 102
  • 139
2

Unfortunately configuration from properties files is static and happens at startup. What I am typically doing is exposing dynamic attributes via :

@ManagedResource
@Service
public class BusinessService {

    @ManagedAttribute
    private int age;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void businessMethod() {
        //use age...
    }

}

Remember to add:

<context:mbean-export/>

To your configuration. Now you can access and change that attribute via jconsole or any other JMX client. See also: 23.3.2 Using Source-Level Metadata (JDK 5.0 annotations).

Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
  • Property files are not necessarily static - they can reside somewhere in the filesystem and be changed by ops. You are right: one can use other mechanisms to update configuration values, but I'd like to use the nice Spring placeholder mechanism. Otherwise - how would you update, say, a timeout for a WebserviceTemplate without much glue code? – Dr. Hans-Peter Störr Apr 05 '12 at 07:26
  • @hstoerr: wrt `WebserviceTemplate` - it requires some glue code even when you don't need to refresh anything: http://onebyteatatime.wordpress.com/2009/03/19/how-to-set-socket-timeout-using-spring-webservicetemplate/ and http://stackoverflow.com/questions/6733744 – Tomasz Nurkiewicz Apr 05 '12 at 09:24
1

There is a running example of what you're trying to accomplish here: https://github.com/ldojo/spring-cloud-config-examples

It demonstrates how a Spring Cloud Config Server and a client service can communicate over Spring Cloud Bus, and the client's configuration properties can change at runtime when there is a configuration change in the Config Server's repo.

Lev
  • 317
  • 2
  • 9
0

For effective runtime reconfiguration, you could use spring's Cloud Config project. In this arrangement, you would have a Configuration Repository, say a git repository, which contains your configuration values. Then put a Configuration Server in front of that repository. This server would be updated whenever a push happens to the backing repository. Finally your apps would be clients of that Config Server and pulls the new configs from it. Check Spring Cloud for more details.

Ali Dehghani
  • 46,221
  • 15
  • 164
  • 151