Search code examples
javaspring-bootrabbitmqspring-amqp

Saving already running listener containers and rolling back if new setting fails


I have the following scenario. My application reads from a configuration file in which I have defined queues, its exclusiveness, thread count and some other details. When application is started, it reads from that configuration and creates DirectMessageListenerContainer for each stated entry. I save these containers in a map in which I associate each of them with a custom name I have given.

On startup, if any failure happens, application fails to run which is what I want. Now, about the problem. I created a reload method which allows users to change configuration without restarting the application through JMX. So, when the configuration file is changed, and if reload method is called, the following process is performed. The validity of new configuration is checked, if it is correct, it is used to set up the new one. To do so, I first, stop all the containers, then destroy them. After that, I initialize new containers. That's it. The issue is that, what happens when an exception occurs on stopping, destroying or any other next step. I handle the exception, but the issue is that it will leave the current setting broken or half-baked. I would like to have a rollback feature, but I am not sure how that can be done. Because after checking the validity of new configuration, I set it as the current one.

I can save the current setting, check if the new one works, if not then I can initialize the previous one, again. However, I can run into another exception when initializing the previous one.

Here's the reload function. RabbitManager is the class I have created, it has nothing special, just does actions like stop, destroy, etc.

public String reloadConfiguration() {
    Rules newRules;

    // checking validity of new rules, setting it, handling exceptions...

    try {
        // setting new rules
        // rules variable saves the current rules 
        rules = newRules;

        // basically calls stop in all the containers
        rabbitManager.stopAll();
        // basically calls destroy in all the containers
        rabbitManager.destroyAllContainers();
        rabbitManager
                .init(rules) // initializes an empty map and sets rules as new rule.
                .registerListeners(); // reads rules and creates DirectMessageListenerContainer for each setting
        log.info("Configuration has been successfully changed, and stopped");
        // returns are for jConsole/monitoring
        return "Configuration has been successfully changed, and stopped";
    } catch (Exception ex) {
        log.error("Exception occurred - "+ex.getMessage(), ex);
        // returns are for jConsole/monitoring
        return "Exception : "+ex.getMessage();
    }
}

I hope my question is clear, if anything is needed or you think the current approach has issues, or maybe I am missing some point, please let me know. Sorry for the vague title, though.


Solution

  • Why don't you just stop() the containers and only destroy them when the new ones are good. If the new config fails, just start() the old containers after destroying the new ones.