Shankar Venkatesan
Shankar Venkatesan

Reputation: 21

Spring Application Context Refresh in webapplication

In my applicaition, during server startup a spring bean DAO class reads values from database and stored in the hash map using spring jdbc template.

I have a requirement to reload a specific set of bean(s) at run time without restarting app servers.

Below is my code snippet. I can post more if needed

private void setMessageDetails() {
messageContentDetails= namedParameterJdbcTemplate.queryForObject(messageContentSql,nameParamMap, new MessageContentRowMapper());
}

MessageContentRowMapper.java maps the resultset values to valuesMap below

public class MessageContentDetails {
private Map<String, Map<String, String>> valuesMap= new HashMap<String, Map<String, String>>();
 ..setters/getters..
} 

Now if any DB values changes, I need to reinitialize MessageContentDetails.java and update hashmap with new values and also autowire again reference of this class where ever it is previously autowired/injected during application startup.

My question is

  1. Can i refresh one or a set of few spring beans using applicationContext.refresh() method.
  2. Is there any better way to reload spring beans that has some static values which needs to be reloaded at run time?
  3. What are the possible thread impacts of using this method. My application is distributed over 3 different data centers with multiple jvms.

Any link towards my above questions will be very helpful.

I am using JAVA 1.6 WebSphere Spring 3.1.0 version and the MessageContentDetails bean is a spring bean configured in spring xml with not annotations.

Upvotes: 2

Views: 8199

Answers (1)

John
John

Reputation: 5287

I am not sure if this can be achieved with Spring out of the box, it might be possible with some tricks and multiple contexts. Spring will by default destroy all Singletons in some context on refresh.

However, considering your case, it is not hard to implement this requirement without Spring involvement.

What you need to do is update a static Map in runtime, that's it :)

General solution:

1) Introduce web service which lets you deliver new values for update,

2) Make beans which depend on updatable beans Observers and offer block-release mechanism,

3)

  • Write general purpose mapping via reflection or polymorphism which lets you update any bean's static values

    or

  • Write specific mapping between web service payload and targeted bean state.

Let's start with 1:

@Controller
public class BeanUpdateController {

@Autowired
private UpdateService updateService;


@RequestMapping(value = "/updateState", method = RequestMethod.POST)
@ResponseBody
public UpdateResponse handleUpdateRequest(@Valid @RequestBody someXMLYouDefine updateRequest) {

    return updateService.execute(voucher);
}

Note that you could have also used RequestMethod.GET and URL parameters.

The idea is however that you transfer name of the Bean you wish to update, field names and appropriate values in some parsable format.

Lets go to 3:

@Component
public class BeanLocator implements ApplicationContextAware {

private static ApplicationContext context;

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    context = applicationContext;   
}

public Object getBean(String beanName)  {

    return context.getBean(beanName);

}

protected ApplicationContext getContext() {

    return context;
}

This service lets you lookup Bean by name which was included in the payload to web service.

Once you acquire the bean, you have a couple of options:

1) Let 'updateable' Beans implement some interface, e.g.Updatable and pass them field names and values which they will update on their own.

2) Use reflection, perhaps like this:

for every field name in payload:

Field field = acquiredBean.getClass().getDeclaredField("fieldNameFromThePayload");

if it is a primitive, you could do:

field.setAccessible(true);
field.set(acquiredBean,"valueFromPayload")

If it is a Collection or a Map and if you use XSD representing this structure, you could directly map list from generated class to target bean. Naturally you could also iterate and do so manually.

Simplest but least flexible way to implement option 3 is to simply pass values as URL parameters on GET request and let service obtain reference to Bean's map which will then be updated.

Now back to second step, which you may skip if it not business critical.

Every bean which might depend on 'updateable' beans should implement Observer interface. When request for update comes, they switch implementation via strategy pattern to queue all incoming requests while there is an update in process. Once bean is updated, it should send acknowledgment. This will trigger another notification for listening services which will unblock queued requests and continue processing.

Web service request for update should be broadcasted to all nodes which have updateable beans.

Upvotes: 2

Related Questions