70

Consider the following scenario. I have a Spring application context with a bean whose properties should be configurable, think DataSource or MailSender. The mutable application configuration is managed by a separate bean, let's call it configuration.

An administrator can now change the configuration values, like email address or database URL, and I would like to re-initialize the configured bean at runtime.

Assume that I can't just simply modify the property of the configurable bean above (e.g. created by FactoryBean or constructor injection) but have to recreate the bean itself.

Any thoughts on how to achieve this? I'd be glad to receive advice on how to organize the whole configuration thing as well. Nothing is fixed. :-)

EDIT

To clarify things a bit: I am not asking how to update the configuration or how to inject static configuration values. I'll try an example:

<beans>
    <util:map id="configuration">
        <!-- initial configuration -->
    </util:map>

    <bean id="constructorInjectedBean" class="Foo">
        <constructor-arg value="#{configuration['foobar']}" />
    </bean>

    <bean id="configurationService" class="ConfigurationService">
        <property name="configuration" ref="configuration" />
    </bean>
</beans>

So there's a bean constructorInjectedBean that uses constructor injection. Imagine the construction of the bean is very expensive so using a prototype scope or a factory proxy is not an option, think DataSource.

What I want to do is that every time the configuration is being updated (via configurationService the bean constructorInjectedBean is being recreated and re-injected into the application context and dependent beans.

We can safely assume that constructorInjectedBean is using an interface so proxy magic is indeed an option.

I hope to have made the question a little bit clearer.

Philipp Jardas
  • 3,222
  • 3
  • 29
  • 42
  • So the `configuration` bean needs to be updated at runtime - or every time the admin changes the values? I that your question? Or do you want the `DataSource` / `MailSender` beans to use the updated configuration at runtime? Or is it both? – madhurtanwani Oct 28 '10 at 09:09
  • It's the second: I want to update the injected configuration values at runtime (see edit in OP). – Philipp Jardas Oct 29 '10 at 10:09

11 Answers11

30

Here is how I have done it in the past: running services which depend on configuration which can be changed on the fly implement a lifecycle interface: IRefreshable:

public interface IRefreshable {
  // Refresh the service having it apply its new values.
  public void refresh(String filter);

  // The service must decide if it wants a cache refresh based on the refresh message filter.
  public boolean requiresRefresh(String filter);
}

Controllers (or services) which can modify a piece of configuration broadcast to a JMS topic that the configuration has changed (supplying the name of the configuration object). A message driven bean then invokes the IRefreshable interface contract on all beans which implement IRefreshable.

The nice thing with spring is that you can automatically detect any service in your application context that needs to be refreshed, removing the need to explicitly configure them:

public class MyCacheSynchService implements InitializingBean, ApplicationContextAware {
 public void afterPropertiesSet() throws Exception {
  Map<String, ?> refreshableServices = m_appCtx.getBeansOfType(IRefreshable.class);
  for (Map.Entry<String, ?> entry : refreshableServices.entrySet() ) {
   Object beanRef = entry.getValue();
   if (beanRef instanceof IRefreshable) {
    m_refreshableServices.add((IRefreshable)beanRef);
   }
  }
 }
}

This approach works particularly well in a clustered application where one of many app servers might change the configuration, which all then need to be aware of. If you want to use JMX as the mechanism for triggering the changes, your JMX bean can then broadcast to the JMS topic when any of its attributes are changed.

Justin
  • 4,437
  • 6
  • 32
  • 52
  • I should also mention that when using JMX you have to do some extra work to get JMX's security to delegate to your application's security model. With the above approach it is less of an issue since the config change is made using your web GUI (assuming you have one) re-using your existing app security model. Since the refresh is simply 'suggested' through JMS, it does not really need to be secured (for this purpose). – Justin Nov 08 '10 at 19:28
  • The problem with this approach is that the refresh should be an atomic operation with regard to other requests being processed by the application. Otherwise a request would be processed with a changed configuration within the middle of its processing. This may cause unpredictable behaviour. So esentially you would have to block requests that use the application context in some way. But than you can just refresh the application context itself. – SpaceTrucker May 26 '16 at 07:46
14

I can think of a 'holder bean' approach (essentially a decorator), where the holder bean delegates to holdee, and it's the holder bean which is injected as a dependency into other beans. Nobody else has a reference to holdee but the holder. Now, when the holder bean's config is changed, it recreates the holdee with this new config and starts delegating to it.

shrini1000
  • 7,038
  • 12
  • 59
  • 99
  • 1
    I open this page just to find this answer. – msangel Sep 12 '13 at 23:51
  • 1
    Know what, after nearly one year I find this answer and it reflects how I've implemented the problem in the end. The decorator implements the required object's interface, registers itself as an observer to the configuration service and re-creates the object in question if necessary. – Philipp Jardas Apr 10 '14 at 07:20
  • @Philipp Jardas thank you for taking time to come back and give an update with the actual implementation that you chose. Much appreciated! – shrini1000 Apr 10 '14 at 15:20
12

You should have a look at JMX. Spring also provides support for this.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
mR_fr0g
  • 8,462
  • 7
  • 39
  • 54
  • Thanks for the suggestion. I have to admit I'm quite a newbie when it comes to JMX. Could you please provide a little hint on how to achieve the behavior I'm looking for? – Philipp Jardas Oct 29 '10 at 10:17
  • @Philipp: JMX with Spring's pretty amazingly easy. You make some javabean-like methods (i.e., get/set pairs) that let you manipulate the value you want to tune, and you annotate the class with `@ManagedResource` and the bean methods with `@ManagedAttribute`. Then you'll be able to connect to the running instance with a JMX client (I use `jvisualvm` with appropriate plugins). – Donal Fellows Nov 03 '10 at 09:32
  • @Donal: Thanks for the answer. However, I still can't see how I could manage the problem I've described above. How would JMX management help in changing the instances of the classes in my application context? – Philipp Jardas Nov 03 '10 at 15:46
  • @Philipp: JMX can reach in and directly invoke methods on (suitably annotated) objects. If you can't see how this could be useful for runtime reconfiguration, well, words escape me… – Donal Fellows Nov 03 '10 at 16:35
  • @Donal: I can plainly see how JMX would help me to recofigure property-configured beans, as do the methods described in some of the other answers. I was, however, asking specificly for a way to reconfigure constructor-injected or bean-factory-created beans, for which JMX does not seem to be more suitable than any of the other methods mentioned. Am I getting this wrong? – Philipp Jardas Nov 08 '10 at 09:39
  • With constructor-injection, you're stuck. Well, you are if we assume that the setting can't be updated through some other mechanism. This is why I tend to prefer using properties for configuring beans (since I can then expose them via JMX with almost no extra effort). I don't know so much about prototype beans – my code hasn't needed them yet (it needs singletons) – but I'd expect them to cause similar problems if they don't do the JMX registration directly in their constructor. I might be wrong on that last bit though. – Donal Fellows Nov 08 '10 at 13:20
  • Sadly often it's not up to me which libraries I have to use. And the constructor-injection paradigm has its upsides. Easy configuration is not one of them... :-( I'm afraid I'm stuck with constructor injection and FactoryBeans. – Philipp Jardas Nov 09 '10 at 18:01
  • @PhilippJardas I have not tried this but can you wrap the constructor injected objects and use jmx property listeners? See: http://docs.spring.io/spring/docs/3.0.x/reference/jmx.html - That is, change a property, the listener picks up the change and then internally reconfigures, or reinstantiates your constuctor injected object. Generally if you can't change the code of the objects you can wrap them and present your wrapper as a bean to the spring context. This would combine the objects in question with the spring context and make them configurable via jmx. – Matt Friedman Apr 08 '14 at 22:39
3

Further updated answer to cover scripted bean

Another approach supported by spring 2.5.x+ is that of the scripted bean. You can use a variety of languages for your script - BeanShell is probably the most intuitive given that it has the same syntax as Java, but it does require some external dependencies. However, the examples are in Groovy.

Section 24.3.1.2 of the Spring Documentation covers how to configure this, but here are some salient excerpts illustrating the approach which I've edited to make them more applicable to your situation:

<beans>

    <!-- This bean is now 'refreshable' due to the presence of the 'refresh-check-delay' attribute -->
    <lang:groovy id="messenger"
          refresh-check-delay="5000" <!-- switches refreshing on with 5 seconds between checks -->
          script-source="classpath:Messenger.groovy">
        <lang:property name="message" value="defaultMessage" />
    </lang:groovy>

    <bean id="service" class="org.example.DefaultService">
        <property name="messenger" ref="messenger" />
    </bean>

</beans>

With the Groovy script looking like this:

package org.example

class GroovyMessenger implements Messenger {

    private String message = "anotherProperty";

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message
    }
}

As the system administrator wants to make changes then they (or you) can edit the contents of the script appropriately. The script is not part of the deployed application and can reference a known file location (or one that is configured through a standard PropertyPlaceholderConfigurer during startup).

Although the example uses a Groovy class, you could have the class execute code that reads a simple properties file. In that manner, you never edit the script directly, just touch it to change the timestamp. That action then triggers the reload, which in turn triggers the refresh of properties from the (updated) properties file, which finally updates the values within the Spring context and off you go.

The documentation does point out that this technique doesn't work for constructor-injection, but maybe you can work around that.

Updated answer to cover dynamic property changes

Quoting from this article, which provides full source code, one approach is:

* a factory bean that detects file system changes
* an observer pattern for Properties, so that file system changes can be propagated
* a property placeholder configurer that remembers where which placeholders were used, and updates singleton beans’ properties
* a timer that triggers the regular check for changed files

The observer pattern is implemented by the interfaces and classes ReloadableProperties, ReloadablePropertiesListener, PropertiesReloadedEvent, and ReloadablePropertiesBase. None of them are especially exciting, just normal listener handling. The class DelegatingProperties serves to transparently exchange the current properties when properties are updated. We only update the whole property map at once, so that the application can avoid inconsistent intermediate states (more on this later).

Now the ReloadablePropertiesFactoryBean can be written to create a ReloadableProperties instance (instead of a Properties instance, as the PropertiesFactoryBean does). When prompted to do so, the RPFB checks file modification times, and if necessary, updates its ReloadableProperties. This triggers the observer pattern machinery.

In our case, the only listener is the ReloadingPropertyPlaceholderConfigurer. It behaves just like a standard spring PropertyPlaceholderConfigurer, except that it tracks all usages of placeholders. Now when properties are reloaded, all usages of each modified property are found, and the properties of those singleton beans are assigned again.

Original answer below covering static property changes:

Sounds like you just want to inject external properties into your Spring context. The PropertyPlaceholderConfigurer is designed for this purpose:

  <!-- Property configuration (if required) -->
  <bean id="serverProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
      <list>
        <!-- Identical properties in later files overwrite earlier ones in this list -->
        <value>file:/some/admin/location/application.properties</value>
      </list>
    </property>
  </bean>

you then reference the external properties with Ant syntax placeholders (that can be nested if you want from Spring 2.5.5 onwards)

  <bean id="example" class="org.example.DataSource">
    <property name="password" value="${password}"/>
  </bean>

You then ensure that the application.properties file is only accessible to the admin user and the user running the application.

Example application.properties:

password=Aardvark

Gary
  • 7,167
  • 3
  • 38
  • 57
  • Thanks for the answer. I'm actually inquiring on how to update the properties at runtime (see edit in OP). – Philipp Jardas Oct 29 '10 at 10:12
  • @Philipp OK, in that case this discussion will probably help (it's for an older version of Spring, but could be updated). Read the extensive comments for notes on how to make it work with Map entries and the like: http://www.wuenschenswert.net/wunschdenken/archives/127 – Gary Oct 29 '10 at 10:21
  • @Gary, thanks for the reply. I've checked the blog you've mentioned and find that I have already created something like this myself. However, I still have no idea how I could not only update properties of beans but replace the bean instances themselves. Let's assume for discussions's sake that we're talking about a property of a `DataSource` that is created with a `FactoryBean`. The mentioned approach would only update the values of the factory bean, which is no help at all. :-( – Philipp Jardas Oct 29 '10 at 14:41
  • @Philipp No problems. As a last gasp attempt at solving your problem you could look at bean scripting (see my edits in the answer for details) – Gary Oct 29 '10 at 15:36
  • @Gary, thanks a lot for the elaborate answer and the interesting approach using scripting. Buuuuuut, you know this is going to come: "doesn't work for constructor-injection" -- you said it yourself. :-) Now what you've proposed actually is a fancy way of setting properties of existing beans. But how do I manage instances that are created with constructor injection or factory beans? – Philipp Jardas Nov 03 '10 at 09:10
  • @Philipp Yeah, I was hoping that you might be able to workaround the constructor injection in your design so that scripting may help. Unfortunately, I'm now at the limit of my knowledge in this area, so I'm going to bow out. Perhaps you should offer a bounty to garner more interest. Best of luck. – Gary Nov 03 '10 at 10:29
2

You can create a custom scope called "reconfigurable" into the ApplicationContext. It creates and caches instances of all beans in this scope. On a configuration change it clears the cache and re-creates the beans on first access with the new configuration. For this to work you need to wrap all instances of reconfigurable beans into an AOP scoped proxy, and access the configuration values with Spring-EL: put a map called config into the ApplicationContext and access the configuration like #{ config['key'] }.

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

Or you could use the approach from this similar question and hence also my solution:

The approach is to have beans that are configured via property files and the solution is to either

  • refresh the entire applicationContext (automatically using a scheduled task or manually using JMX) when properties have changed or
  • use a dedicated property provider object to access all properties. This property provider will keep checking the properties files for modification. For beans where prototype-based property lookup is impossible, register a custom event that your property provider will fire when it finds an updated property file. Your beans with complicated lifecycles will need to listen for that event and refresh themselves.
Community
  • 1
  • 1
Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
1

This is not something I tried, I am trying to provide pointers.

Assuming your application context is a subclass of AbstractRefreshableApplicationContext(example XmlWebApplicationContext, ClassPathXmlApplicationContext). AbstractRefreshableApplicationContext.getBeanFactory() will give you instance of ConfigurableListableBeanFactory. Check if it is instance of BeanDefinitionRegistry. If so you can call 'registerBeanDefinition' method. This approach will be tightly coupled with Spring implementation,

Check the code of AbstractRefreshableApplicationContext and DefaultListableBeanFactory(this is the implementation you get when you call 'AbstractRefreshableApplicationContext getBeanFactory()')

Adisesha
  • 5,200
  • 1
  • 32
  • 43
  • This might acutally be quite a nice idea. The interface for `registerBeanDefinition()` is `org.springframework.beans.factory.support.BeanDefinitionRegistry`, by the way. I will look into this, thanks. – Philipp Jardas Nov 09 '10 at 17:59
  • Yes.Also do not forget to set 'allowBeanDefinitionOverriding' on application context. – Adisesha Nov 09 '10 at 20:05
0

You may want to have a look at the Spring Inspector a plug-gable component that provides programmatic access to any Spring based application at run-time. You can use Javascript to change configurations or manage the application behaviour at run-time.

Julio
  • 720
  • 7
  • 16
0

Here is the nice idea of writing your own PlaceholderConfigurer that tracks the usage of properties and changes them whenever a configuration change occurs. This has two disadvantages, though:

  1. It does not work with constructor injection of property values.
  2. You can get race conditions if the reconfigured bean receives a changed configuration while it is processing some stuff.
Dr. Hans-Peter Störr
  • 25,298
  • 30
  • 102
  • 139
0

My solution was to copy the original object. Fist i created an interface

/**
 * Allows updating data to some object.
 * Its an alternative to {@link Cloneable} when you cannot 
 * replace the original pointer. Ex.: Beans 
 * @param <T> Type of Object
 */
public interface Updateable<T>
{
    /**
     * Import data from another object
     * @param originalObject Object with the original data
     */
    public void copyObject(T originalObject);
}

For easing the implementation of the function fist create a constructor with all fields, so the IDE could help me a bit. Then you can make a copy constructor that uses the same function Updateable#copyObject(T originalObject). You can also profit of the code of the constructor created by the IDE to create the function to implement:

public class SettingsDTO implements Cloneable, Updateable<SettingsDTO>
{
    private static final Logger LOG = LoggerFactory.getLogger(SettingsDTO.class);

    @Size(min = 3, max = 30)  
    private String id;

    @Size(min = 3, max = 30)
    @NotNull 
    private String name;

    @Size(min = 3, max = 100)
    @NotNull 
    private String description;

    @Max(100)
    @Min(5) 
    @NotNull
    private Integer pageSize;

    @NotNull 
    private String dateFormat; 

    public SettingsDTO()
    { 
    }   

    public SettingsDTO(String id, String name, String description, Integer pageSize, String dateFormat)
    {
        this.id = id;
        this.name = name;
        this.description = description;
        this.pageSize = pageSize;
        this.dateFormat = dateFormat;
    }

    public SettingsDTO(SettingsDTO original)
    {
        copyObject(original);
    }

    @Override
    public void copyObject(SettingsDTO originalObject)
    {
        this.id = originalObject.id;
        this.name = originalObject.name;
        this.description = originalObject.description;
        this.pageSize = originalObject.pageSize;
        this.dateFormat = originalObject.dateFormat;
    } 
}

I used it in a Controller for updating the current settings for the app:

        if (bindingResult.hasErrors())
        {
            model.addAttribute("settingsData", newSettingsData);
            model.addAttribute(Templates.MSG_ERROR, "The entered data has errors");
        }
        else
        {
            synchronized (settingsData)
            {
                currentSettingData.copyObject(newSettingsData);
                redirectAttributes.addFlashAttribute(Templates.MSG_SUCCESS, "The system configuration has been updated successfully");
                return String.format("redirect:/%s", getDao().getPath());
            }
        }

So the currentSettingsData which has the configuration of the application gonna have the updated values, located in newSettingsData. These method allows updating any bean without high complexity.

EliuX
  • 11,389
  • 6
  • 45
  • 40
0

Option 1 :

  1. Inject the configurable bean into the DataSource or MailSender. Always get the configurable values from the configuration bean from within these beans.
  2. Inside the configurable bean run a thread to read the externally configurable properties (file etc..) periodically. This way the configurable bean will refresh itself after the admin had changed the properties and so the DataSource will get the updated values automatically.

Option 2 (bad, i think, but maybe not - depends on use case) :

  1. Always create new beans for beans of type DataSource / MailSender - using prototype scope. In the init of the bean, read the properties afresh.

Option 3 : I think, @mR_fr0g suggestion on using JMX might not be a bad idea. What you could do is :

  1. expose your configuration bean as a MBean (read http://static.springsource.org/spring/docs/2.5.x/reference/jmx.html)
  2. Ask your admin to change the configuration properties on the MBean (or provide an interface in the bean to trigger property updates from their source)
  3. This MBean (a new piece of java code that you will need to write), MUST keep references of Beans (the ones that you want to change / inject the changed properties into). This should be simple (via setter injection or runtime fetch of bean names / classes)
    1. When the property on the MBean is changed (or triggered), it must call the appropriate setters on the respective beans. That way, your legacy code does not change, you can still manage runtime property changes.

HTH!

madhurtanwani
  • 1,199
  • 7
  • 13
  • Also I'm hoping you've read http://stackoverflow.com/questions/2008175/apply-dynamic-properties-to-a-bean-at-runtime – madhurtanwani Oct 28 '10 at 09:22
  • Option 1 will probably not work if I have no control over the configured classes. And if I had control over them they would be "polluted" by configuration code which sounds not good to me. Option 2 is not an option in my case because bean lifecycle is expensive. You wouldn't want to apply a prototype scope to a DataSource... please see OP for an edit with clarifications. – Philipp Jardas Oct 29 '10 at 10:14
  • The referenced question does not seem to hold an answer to my problem... – Philipp Jardas Oct 29 '10 at 10:16
  • I'm not sure if "polluted" is even correct - you have classes that need externally defined configuration. Now, either you must "inject" the properties (as they change) into these classes (in which case I think the class injecting the prop must know of such classes needing the configuration) OR those classes must pull the changes as they happen - probably via a central config manager – madhurtanwani Oct 29 '10 at 12:23
  • I've thought about the pollution aspect and think you are right, there will probably no way to handle this problem without mixing configuration aspects into the classes. However, I still have no idea how to update beans that are created using constructor injection or factory beans. :-( – Philipp Jardas Nov 03 '10 at 09:11
  • @Phillip - I've added another section to the answer, using the JMX suggestion (from mr_fr0g). BTW, if your requirement is SPECIFICALLY to *recreate and re-inject (bean) into the application context and dependent beans* - then I dont think its a good idea (not sure if its possible). You should aim to work alongside with the Spring framework and not inside it. HTH! All the best! – madhurtanwani Nov 03 '10 at 11:28