Skip to content

Set value of isAutoStartup for DefaultMessageListenerContainerCoordinator  #273

@stephencassidy-r7

Description

@stephencassidy-r7

Hi,

Another edge case! As part of our applications, we use netflix-archaius for dynamic configuration at runtime - in this case, to control whether a queue listener is enabled or not, and to change if needed.

To do this, I have extended the DefaultMessageListenerContainerCoordinator to accept a small properties holder class that is used to determine the value of isAutoStartup, and add callbacks to properties to call start|stopContainer(identifier) to try and match the pattern used for creating other containers etc -

interface MessageListenerContainerCoordinatorProperties {
    /**
     * @return Whether the {@link MessageListenerContainerCoordinator}'s {@link MessageListenerContainerCoordinator#startAllContainers()} will be invoked by Spring.
     */
    boolean isAutoStartContainersEnabled();
}

@Value
@Builder(toBuilder = true)
class DefaultMessageListenerContainerCoordinatorProperties implements MessageListenerContainerCoordinatorProperties {
    Boolean isAutoStartContainersEnabled;
    
    @Override
    public boolean isAutoStartContainersEnabled() {
        return isAutoStartContainersEnabled;
    }
}

class ExtendedDefaultMessageListenerContainerCoordinator extends DefaultMessageListenerContainerCoordinator {
    private final MessageListenerContainerCoordinatorProperties properties;

    public ExtendedDefaultMessageListenerContainerCoordinator(final List<MessageListenerContainerFactory> factories, final MessageListenerContainerCoordinatorProperties properties) {
        super(factories);

        this.properties = properties;
    }

   // Used to get a list of all containers so we can build the property callbacks...
    public Collection<String> getContainerIdentifiers() {
        return getContainers().stream()
            .map(MessageListenerContainer::getIdentifier)
            .collect(toUnmodifiableSet());
    }

    @Override
    public boolean isAutoStartup() {
        return properties.isAutoStartContainersEnabled();
    }
}

class MessageListenerContainerLifecycleController implements SmartLifecycle {
    private final DefaultMessageListenerContainerCoordinator coordinator;
    private final Map<String, DynamicBooleanProperty> containerEnabled;

    public MessageListenerContainerLifecycleController(final DefaultMessageListenerContainerCoordinator coordinator) {
        this.coordinator = coordinator;
        /**
         * Create all the properties and callbacks for each container identifier returned from 
         * {@link ExtendedDefaultMessageListenerContainerCoordinator#getContainerIdentifiers()} on each to invoke the 
         * @{@link MessageListenerContainerCoordinator#startContainer(String)} or 
         * @{@link MessageListenerContainerCoordinator#stopContainer(String)}} depending on the value...
         */
        this.containerEnabled = createContainerLifecycleCallbacks(coordinator);
    }

    @Override
    public void start() {
        containerEnabled.forEach((identifier, enabled) -> {
            if (enabled.getValue()) {
                coordinator.startContainer(identifier);
            }
        });
    }

    @Override
    public void stop() {
        coordinator.stopAllContainers();
    }

    @Override
    public boolean isRunning() {}
}

Another tweak that would help is being able to return a collection of all identifiers if possible 😄 Currently I have these classes sitting under the same package as the original to access the getContainers() method - other than this the library does everything I needed and more, nice to get a consumer going with a few annotations!

If there is a better way to raise these let me know - technically this is not an issue or bug!

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions