org.springframework.integration.support.SmartLifecycleRoleController.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.integration.support.SmartLifecycleRoleController.java

Source

/*
 * Copyright 2015-2017 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.integration.support;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.Lifecycle;
import org.springframework.context.SmartLifecycle;
import org.springframework.integration.leader.event.AbstractLeaderEvent;
import org.springframework.integration.leader.event.OnGrantedEvent;
import org.springframework.integration.leader.event.OnRevokedEvent;
import org.springframework.integration.support.context.NamedComponent;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

/**
 * Bulk start/stop {@link SmartLifecycle} in a particular role in phase order.
 *
 * @author Gary Russell
 * @author Artem Bilan
 *
 * @since 4.2
 *
 */
public class SmartLifecycleRoleController
        implements ApplicationListener<AbstractLeaderEvent>, ApplicationContextAware {

    private static final Log logger = LogFactory.getLog(SmartLifecycleRoleController.class);

    private final MultiValueMap<String, SmartLifecycle> lifecycles = new LinkedMultiValueMap<String, SmartLifecycle>();

    private final MultiValueMap<String, String> lazyLifecycles = new LinkedMultiValueMap<String, String>();

    private ApplicationContext applicationContext;

    /**
     * Construct an instance with the provided lists of roles and lifecycles, which must be of equal length.
     * @param roles the roles.
     * @param lifecycles the lifecycles corresponding to the roles.
     */
    public SmartLifecycleRoleController(List<String> roles, List<SmartLifecycle> lifecycles) {
        Assert.notNull(roles, "'roles' cannot be null");
        Assert.notNull(lifecycles, "'lifecycles' cannot be null");
        Assert.isTrue(roles.size() == lifecycles.size(), "'roles' and 'lifecycles' must be the same lenght");
        Iterator<SmartLifecycle> iterator = lifecycles.iterator();
        for (String role : roles) {
            SmartLifecycle lifecycle = iterator.next();
            addLifecycleToRole(role, lifecycle);
        }
    }

    /**
     * Construct an instance with the provided map of roles/instances.
     * @param lifcycles the {@link MultiValueMap} of beans in roles.
     */
    public SmartLifecycleRoleController(MultiValueMap<String, SmartLifecycle> lifcycles) {
        for (Entry<String, List<SmartLifecycle>> lifecyclesInRole : lifcycles.entrySet()) {
            String role = lifecyclesInRole.getKey();
            for (SmartLifecycle lifecycle : lifecyclesInRole.getValue()) {
                addLifecycleToRole(role, lifecycle);
            }
        }
    }

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

    /**
     * Add a {@link SmartLifecycle} to the role.
     * @param role the role.
     * @param lifecycle the {@link SmartLifecycle}.
     */
    public final void addLifecycleToRole(String role, SmartLifecycle lifecycle) {
        this.lifecycles.add(role, lifecycle);
    }

    /**
     * Add a {@link SmartLifecycle} bean to the role using its name.
     * @param role the role.
     * @param lifecycleBeanName the bean name of the {@link SmartLifecycle}.
     */
    public void addLifecycleToRole(String role, String lifecycleBeanName) {
        Assert.state(this.applicationContext != null, "An application context is required to use this method");
        this.lazyLifecycles.add(role, lifecycleBeanName);
    }

    /**
     * Add a {@link SmartLifecycle} beans to the role using their names.
     * @param role the role.
     * @param lifecycleBeanNames the bean names of the {@link SmartLifecycle}s.
     */
    public void addLifecyclesToRole(String role, List<String> lifecycleBeanNames) {
        Assert.state(this.applicationContext != null, "An application context is required to use this method");
        for (String lifecycleBeanName : lifecycleBeanNames) {
            this.lazyLifecycles.add(role, lifecycleBeanName);
        }
    }

    /**
     * Start all registered {@link SmartLifecycle}s in the role.
     * @param role the role.
     */
    public void startLifecyclesInRole(String role) {
        if (this.lazyLifecycles.size() > 0) {
            addLazyLifecycles();
        }
        List<SmartLifecycle> lifecycles = this.lifecycles.get(role);
        if (lifecycles != null) {
            lifecycles = new ArrayList<SmartLifecycle>(lifecycles);
            Collections.sort(lifecycles,
                    (o1, o2) -> o1.getPhase() < o2.getPhase() ? -1 : o1.getPhase() > o2.getPhase() ? 1 : 0);
            if (logger.isDebugEnabled()) {
                logger.debug("Starting " + lifecycles + " in role " + role);
            }
            for (SmartLifecycle lifecycle : lifecycles) {
                try {
                    lifecycle.start();
                } catch (Exception e) {
                    logger.error("Failed to start " + lifecycle + " in role " + role, e);
                }
            }
        } else {
            if (logger.isDebugEnabled()) {
                logger.debug("No components in role " + role + ". Nothing to start");
            }
        }
    }

    /**
     * Stop all registered {@link SmartLifecycle}s in the role.
     * @param role the role.
     */
    public void stopLifecyclesInRole(String role) {
        if (this.lazyLifecycles.size() > 0) {
            addLazyLifecycles();
        }
        List<SmartLifecycle> lifecycles = this.lifecycles.get(role);
        if (lifecycles != null) {
            lifecycles = new ArrayList<SmartLifecycle>(lifecycles);
            Collections.sort(lifecycles,
                    (o1, o2) -> o1.getPhase() < o2.getPhase() ? 1 : o1.getPhase() > o2.getPhase() ? -1 : 0);
            if (logger.isDebugEnabled()) {
                logger.debug("Stopping " + lifecycles + " in role " + role);
            }
            for (SmartLifecycle lifecycle : lifecycles) {
                try {
                    lifecycle.stop();
                } catch (Exception e) {
                    logger.error("Failed to stop " + lifecycle + " in role " + role, e);
                }
            }
        } else {
            if (logger.isDebugEnabled()) {
                logger.debug("No components in role " + role + ". Nothing to stop");
            }
        }
    }

    /**
     * Return a collection of the roles currently managed by this controller.
     * @return the roles.
     * @since 4.3.8
     */
    public Collection<String> getRoles() {
        if (this.lazyLifecycles.size() > 0) {
            addLazyLifecycles();
        }
        return new ArrayList<>(this.lifecycles.keySet());
    }

    /**
     * Return true if all endpoints in the role are running.
     * @param role the role.
     * @return true if at least one endpoint in the role, and all are running.
     * @since 4.3.8
     */
    public boolean allEndpointsRunning(String role) {
        Map<String, Boolean> status = getEndpointsRunningStatus(role);
        return !status.isEmpty() && status.values().stream().allMatch(b -> b);
    }

    /**
     * Return true if none of the endpoints in the role are running or if
     * there are no endpoints in the role.
     * @param role the role.
     * @return true if there are no endpoints or none are running.
     * @since 4.3.8
     */
    public boolean noEndpointsRunning(String role) {
        Map<String, Boolean> status = getEndpointsRunningStatus(role);
        return status.isEmpty() || status.values().stream().noneMatch(b -> b);
    }

    /**
     * Return the running status of each endpoint in the role.
     * @param role the role.
     * @return A map of component names : running status
     * @since 4.3.8
     */
    public Map<String, Boolean> getEndpointsRunningStatus(String role) {
        if (this.lazyLifecycles.size() > 0) {
            addLazyLifecycles();
        }
        if (!this.lifecycles.containsKey(role)) {
            return Collections.emptyMap();
        }

        AtomicInteger index = new AtomicInteger();
        return this.lifecycles.get(role).stream()
                .collect(Collectors.toMap(
                        e -> (e instanceof NamedComponent) ? ((NamedComponent) e).getComponentName()
                                : (e.getClass().getSimpleName() + "#" + index.getAndIncrement()),
                        Lifecycle::isRunning));
    }

    private synchronized void addLazyLifecycles() {
        for (Entry<String, List<String>> entry : this.lazyLifecycles.entrySet()) {
            doAddLifecyclesToRole(entry.getKey(), entry.getValue());
        }
        this.lazyLifecycles.clear();
    }

    private void doAddLifecyclesToRole(String role, List<String> lifecycleBeanNames) {
        for (String lifecycleBeanName : lifecycleBeanNames) {
            try {
                SmartLifecycle lifecycle = this.applicationContext.getBean(lifecycleBeanName, SmartLifecycle.class);
                addLifecycleToRole(role, lifecycle);
            } catch (NoSuchBeanDefinitionException e) {
                logger.warn("Skipped; no such bean: " + lifecycleBeanName);
            }
        }
    }

    @Override
    public void onApplicationEvent(AbstractLeaderEvent event) {
        if (event instanceof OnGrantedEvent) {
            startLifecyclesInRole(event.getRole());
        } else if (event instanceof OnRevokedEvent) {
            stopLifecyclesInRole(event.getRole());
        }
    }

}