org.nanoframework.concurrent.scheduler.SchedulerFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.nanoframework.concurrent.scheduler.SchedulerFactory.java

Source

/*
 * Copyright 2015-2016 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.nanoframework.concurrent.scheduler;

import static org.nanoframework.core.context.ApplicationContext.Scheduler.BASE_PACKAGE;
import static org.nanoframework.core.context.ApplicationContext.Scheduler.ETCD_ENABLE;
import static org.nanoframework.core.context.ApplicationContext.Scheduler.EXCLUSIONS;
import static org.nanoframework.core.context.ApplicationContext.Scheduler.INCLUDES;
import static org.nanoframework.core.context.ApplicationContext.Scheduler.SHUTDOWN_TIMEOUT;

import java.text.ParseException;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.lang3.StringUtils;
import org.nanoframework.commons.loader.LoaderException;
import org.nanoframework.commons.loader.PropertiesLoader;
import org.nanoframework.commons.support.logging.Logger;
import org.nanoframework.commons.support.logging.LoggerFactory;
import org.nanoframework.commons.util.Assert;
import org.nanoframework.commons.util.CollectionUtils;
import org.nanoframework.commons.util.ObjectCompare;
import org.nanoframework.commons.util.RuntimeUtil;
import org.nanoframework.core.component.scan.ComponentScan;
import org.nanoframework.core.globals.Globals;
import org.nanoframework.concurrent.exception.SchedulerException;
import org.nanoframework.concurrent.scheduler.defaults.etcd.EtcdOrderWatcherScheduler;
import org.nanoframework.concurrent.scheduler.defaults.etcd.EtcdScheduler;
import org.nanoframework.concurrent.scheduler.defaults.etcd.EtcdSchedulerOperate;
import org.nanoframework.concurrent.scheduler.defaults.monitor.LocalJmxMonitorScheduler;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.inject.Injector;

/**
 * .
 * 
 * @author yanghe
 * @since 1.3
 */
public class SchedulerFactory {
    public static final String DEFAULT_SCHEDULER_NAME_PREFIX = "Scheduler-Thread-Pool: ";
    public static final SchedulerThreadFactory THREAD_FACTORY = new SchedulerThreadFactory();

    private static final Logger LOGGER = LoggerFactory.getLogger(SchedulerFactory.class);
    private static SchedulerFactory FACTORY;
    private static final Object LOCK = new Object();
    private static boolean IS_LOADED = false;

    private static final ThreadPoolExecutor service = (ThreadPoolExecutor) Executors
            .newCachedThreadPool(THREAD_FACTORY);

    private static EtcdSchedulerOperate ETCD_SCHEDULER;

    private final ConcurrentMap<String, BaseScheduler> startedScheduler = new ConcurrentHashMap<>();
    private final ConcurrentMap<String, BaseScheduler> stoppingScheduler = new ConcurrentHashMap<>();
    private final ConcurrentMap<String, BaseScheduler> stoppedScheduler = new ConcurrentHashMap<>();
    private final ConcurrentMap<String, Set<BaseScheduler>> group = new ConcurrentHashMap<>();

    private final long shutdownTimeout = Long.parseLong(System.getProperty(SHUTDOWN_TIMEOUT, "60000"));

    private SchedulerFactory() {

    }

    public static SchedulerFactory getInstance() {
        synchronized (LOCK) {
            if (FACTORY == null) {
                FACTORY = new SchedulerFactory();
                final StatusMonitorScheduler statusMonitor = FACTORY.new StatusMonitorScheduler();
                statusMonitor.getConfig().getService().execute(statusMonitor);
                Runtime.getRuntime().addShutdownHook(new Thread(FACTORY.new ShutdownHook()));
            }
        }

        return FACTORY;
    }

    /**
     * 
     * 
     * @param scheduler 
     * @return ?
     */
    public BaseScheduler bind(final BaseScheduler scheduler) {
        try {
            scheduler.setClose(false);
            startedScheduler.put(scheduler.getConfig().getId(), scheduler);
            return scheduler;
        } finally {
            LOGGER.info(": ?[ {} ]", scheduler.getConfig().getId());
        }
    }

    /**
     * 
     * 
     * @param scheduler 
     * @return ?
     */
    protected BaseScheduler unbind(final BaseScheduler scheduler) {
        final BaseScheduler removedScheduler = startedScheduler.remove(scheduler.getConfig().getId());
        if (removedScheduler != null) {
            LOGGER.debug(" : ?[ {} ], : {}", scheduler.getConfig().getId(),
                    startedScheduler.size());
        }

        return scheduler;
    }

    /**
     * ?
     * @return 
     */
    public int getStartedSchedulerSize() {
        return startedScheduler.size();
    }

    /**
     * 
     * @return ?
     */
    public Collection<BaseScheduler> getStartedScheduler() {
        return startedScheduler.values();
    }

    public int getStoppedSchedulerSize() {
        return stoppedScheduler.size();
    }

    public Collection<BaseScheduler> getStoppedScheduler() {
        return stoppedScheduler.values();
    }

    public int getStoppingSchedulerSize() {
        return stoppingScheduler.size();
    }

    public Collection<BaseScheduler> getStoppingScheduler() {
        return stoppingScheduler.values();
    }

    /**
     * 
     * @param id ?
     */
    public void close(final String id) {
        try {
            final BaseScheduler scheduler = startedScheduler.get(id);
            close(scheduler);
        } finally {
            LOGGER.info(", ?: {}", id);
        }
    }

    public void close(final BaseScheduler scheduler) {
        if (scheduler != null && !scheduler.isClose()) {
            /** Sync to Etcd by stop method */
            ETCD_SCHEDULER.stopping(scheduler.getConfig().getGroup(), scheduler.getConfig().getId(),
                    scheduler.getAnalysis());

            scheduler.setClose(true);
            stoppingScheduler.put(scheduler.getConfig().getId(), scheduler);
            startedScheduler.remove(scheduler.getConfig().getId(), scheduler);
        }
    }

    /**
     * 
     * @param groupName the groupName
     */
    public void closeGroup(final String groupName) {
        Assert.hasLength(groupName, "groupName must not be null");
        final Set<String> ids = Sets.newHashSet();
        startedScheduler.forEach((id, scheduler) -> {
            if (groupName.equals(scheduler.getConfig().getGroup())) {
                if (!scheduler.isClose()) {
                    /** Sync to Etcd by stop method */
                    ETCD_SCHEDULER.stopping(scheduler.getConfig().getGroup(), scheduler.getConfig().getId(),
                            scheduler.getAnalysis());

                    scheduler.setClose(true);
                    stoppingScheduler.put(scheduler.getConfig().getId(), scheduler);
                    ids.add(scheduler.getConfig().getId());
                }
            }
        });

        ids.forEach(id -> startedScheduler.remove(id));
    }

    /**
     * 
     */
    public void closeAll() {
        if (startedScheduler.size() > 0) {
            LOGGER.warn("");
            startedScheduler.keySet().forEach(id -> {
                try {
                    final BaseScheduler scheduler = startedScheduler.get(id);
                    if (scheduler != null && !scheduler.isClose()) {
                        /** Sync to Etcd by stop method */
                        ETCD_SCHEDULER.stopping(scheduler.getConfig().getGroup(), scheduler.getConfig().getId(),
                                scheduler.getAnalysis());

                        scheduler.setClose(true);
                        stoppingScheduler.put(scheduler.getConfig().getId(), scheduler);
                    }

                } finally {
                    LOGGER.debug(", ?: {}", id);
                }
            });

            startedScheduler.clear();
        }
    }

    /**
     * ??
     */
    public void startAll() {
        if (stoppedScheduler.size() > 0) {
            stoppedScheduler.forEach((id, scheduler) -> {
                LOGGER.info("Start scheduler [ {} ], class with [ {} ]", id, scheduler.getClass().getName());

                bind(scheduler);
                THREAD_FACTORY.setBaseScheduler(scheduler);
                service.execute(scheduler);

                /** Sync to Etcd by start method */
                final SchedulerConfig conf = scheduler.getConfig();
                ETCD_SCHEDULER.start(conf.getGroup(), conf.getId(), scheduler.getAnalysis());
            });

            stoppedScheduler.clear();
        }
    }

    public void startGroup(final String groupName) {
        if (stoppedScheduler.size() > 0) {
            final Set<String> keys = Sets.newHashSet();
            stoppedScheduler.forEach((id, scheduler) -> {
                if (groupName.equals(scheduler.getConfig().getGroup())) {
                    if (scheduler.isClose()) {
                        LOGGER.info("Start scheduler [ {} ], class with [ {} ]", id,
                                scheduler.getClass().getName());

                        bind(scheduler);
                        THREAD_FACTORY.setBaseScheduler(scheduler);
                        service.execute(scheduler);
                        keys.add(id);

                        /** Sync to Etcd by start method */
                        final SchedulerConfig conf = scheduler.getConfig();
                        ETCD_SCHEDULER.start(conf.getGroup(), conf.getId(), scheduler.getAnalysis());
                    }
                }
            });

            for (String key : keys) {
                stoppedScheduler.remove(key);
            }
        }
    }

    public void start(final String id) {
        final BaseScheduler scheduler = stoppedScheduler.get(id);
        if (scheduler != null && scheduler.isClose()) {
            LOGGER.info("Start scheduler [ {} ], class with [ {} ]", id, scheduler.getClass().getName());

            bind(scheduler);
            THREAD_FACTORY.setBaseScheduler(scheduler);
            service.execute(scheduler);
            stoppedScheduler.remove(id);

            /** Sync to Etcd by start method */
            final SchedulerConfig conf = scheduler.getConfig();
            ETCD_SCHEDULER.start(conf.getGroup(), conf.getId(), scheduler.getAnalysis());
        }
    }

    public void append(final String groupName, final int size, final boolean autoStart) {
        final BaseScheduler scheduler = findLast(groupName);
        if (scheduler == null) {
            return;
        }

        for (int idx = 0; idx < size; idx++) {
            final SchedulerConfig conf = (SchedulerConfig) scheduler.getConfig().clone();
            int total = conf.getTotal();
            conf.setTotal(total + 1);
            conf.setNum(total);
            conf.setId(groupName + '-' + scheduler.getIndex(groupName));
            conf.setName(DEFAULT_SCHEDULER_NAME_PREFIX + conf.getId());

            final BaseScheduler newScheduler = scheduler.clone();
            newScheduler.setClose(true);
            newScheduler.setClosed(true);
            newScheduler.setRemove(false);
            newScheduler.setConfig(conf);
            addScheduler(newScheduler);
            if (autoStart) {
                start(conf.getId());
            } else {
                /** Sync to Etcd by start method */
                final SchedulerConfig newScheConf = newScheduler.getConfig();
                ETCD_SCHEDULER.stopped(newScheConf.getGroup(), newScheConf.getId(), false, scheduler.getAnalysis());
            }
        }
    }

    public boolean closed(final String id) {
        return stoppedScheduler.containsKey(id);
    }

    public boolean started(final String id) {
        return startedScheduler.containsKey(id);
    }

    public boolean hasClosedGroup(final String group) {
        if (stoppedScheduler.size() > 0) {
            final Collection<BaseScheduler> schedulers = stoppedScheduler.values();
            for (final BaseScheduler scheduler : schedulers) {
                final SchedulerConfig conf = scheduler.getConfig();
                if (conf.getGroup().equals(group)) {
                    return true;
                }
            }
        }

        return false;
    }

    public boolean hasStartedGroup(final String group) {
        if (startedScheduler.size() > 0) {
            for (final BaseScheduler scheduler : startedScheduler.values()) {
                final SchedulerConfig conf = scheduler.getConfig();
                if (conf.getGroup().equals(group)) {
                    return true;
                }
            }
        }

        return false;
    }

    public void addScheduler(final BaseScheduler scheduler) {
        Set<BaseScheduler> groupScheduler = group.get(scheduler.getConfig().getGroup());
        if (groupScheduler == null) {
            groupScheduler = Sets.newLinkedHashSet();
        }

        groupScheduler.add(scheduler);
        group.put(scheduler.getConfig().getGroup(), groupScheduler);

        if (stoppedScheduler.containsKey(scheduler.getConfig().getId())
                || startedScheduler.containsKey(scheduler.getConfig().getId())) {
            throw new SchedulerException("exists scheduler in memory");
        }

        stoppedScheduler.put(scheduler.getConfig().getId(), scheduler);
        rebalance(scheduler.getConfig().getGroup());
    }

    public int removeScheduler(final BaseScheduler scheduler, final boolean force) {
        final Set<BaseScheduler> groupScheduler = group.get(scheduler.getConfig().getGroup());
        boolean remove = false;
        if (groupScheduler.size() > 1 || force) {
            groupScheduler.remove(scheduler);
            scheduler.setRemove(remove = true);

            // ???stoppedScheduler
            stoppedScheduler.remove(scheduler.getConfig().getId(), scheduler);
        }

        if (scheduler.isClosed()) {
            /** Sync to Etcd by start method */
            final SchedulerConfig conf = scheduler.getConfig();
            ETCD_SCHEDULER.stopped(conf.getGroup(), conf.getId(), remove, scheduler.getAnalysis());
        } else {
            close(scheduler.getConfig().getId());
        }

        rebalance(scheduler.getConfig().getGroup());
        return groupScheduler.size();
    }

    public int removeScheduler(final BaseScheduler scheduler) {
        return removeScheduler(scheduler, false);
    }

    public int removeScheduler(final String groupName) {
        final BaseScheduler scheduler = findLast(groupName);
        if (scheduler != null) {
            return removeScheduler(scheduler);
        }

        return 0;
    }

    public final void removeGroup(final String groupName) {
        while (removeScheduler(groupName) > 1) {
            ;
        }

        closeGroup(groupName);
    }

    public int getGroupSize(final String groupName) {
        final Set<BaseScheduler> groupScheduler = group.get(groupName);
        if (!CollectionUtils.isEmpty(groupScheduler)) {
            return groupScheduler.size();
        }

        return 0;
    }

    public Set<BaseScheduler> getGroupScheduler(final String groupName) {
        return group.get(groupName);
    }

    public final BaseScheduler find(final String id) {
        Assert.hasLength(id, "id must be not empty.");
        final String groupName = id.substring(0, id.lastIndexOf('-'));
        final Set<BaseScheduler> groupScheduler = group.get(groupName);
        if (!CollectionUtils.isEmpty(groupScheduler)) {
            for (final BaseScheduler scheduler : groupScheduler) {
                final SchedulerConfig conf = scheduler.getConfig();
                if (conf.getId().equals(id)) {
                    return scheduler;
                }
            }
        }

        return null;
    }

    public BaseScheduler findLast(final String groupName) {
        Assert.hasLength(groupName);
        final Set<BaseScheduler> groupScheduler = group.get(groupName);
        if (!CollectionUtils.isEmpty(groupScheduler)) {
            int max = -1;
            for (final BaseScheduler scheduler : groupScheduler) {
                final SchedulerConfig conf = scheduler.getConfig();
                if (conf.getNum() > max) {
                    max = scheduler.getConfig().getNum();
                }
            }

            for (final BaseScheduler scheduler : groupScheduler) {
                final SchedulerConfig conf = scheduler.getConfig();
                if (conf.getNum() == max) {
                    return scheduler;
                }
            }
        }

        return null;
    }

    public void rebalance(final String groupName) {
        Assert.hasLength(groupName);
        final Set<BaseScheduler> groupScheduler = group.get(groupName);
        if (!CollectionUtils.isEmpty(groupScheduler)) {
            final AtomicInteger idx = new AtomicInteger(0);
            groupScheduler.forEach(scheduler -> {
                scheduler.getConfig().setNum(idx.getAndIncrement());
                scheduler.getConfig().setTotal(groupScheduler.size());
            });
        }
    }

    /**
     * 
     * @throws IllegalArgumentException ??
     * @throws IllegalAccessException ?
     */
    public static final void load() throws IllegalArgumentException, IllegalAccessException {
        if (IS_LOADED) {
            throw new LoaderException("Scheduler?????");
        }

        if (PropertiesLoader.PROPERTIES.size() == 0) {
            throw new LoaderException(", .");
        }

        final Set<String> includes = Sets.newLinkedHashSet();
        final Set<String> exclusions = Sets.newLinkedHashSet();
        PropertiesLoader.PROPERTIES.values().stream().filter(item -> item.get(BASE_PACKAGE) != null)
                .forEach(item -> {
                    final String basePacakge = item.getProperty(BASE_PACKAGE);
                    ComponentScan.scan(basePacakge);
                });

        PropertiesLoader.PROPERTIES.values().stream().forEach(item -> {
            if (item.containsKey(INCLUDES)) {
                String[] include = item.getProperty(INCLUDES, ".").split(",");
                for (String inc : include) {
                    includes.add(inc);
                }
            }

            if (item.containsKey(EXCLUSIONS)) {
                String[] exclusion = item.getProperty(EXCLUSIONS, "").split(",");
                for (String exc : exclusion) {
                    exclusions.add(exc);
                }
            }
        });

        final Set<Class<?>> componentClasses = ComponentScan.filter(Scheduler.class);
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Scheduler size: {}", componentClasses.size());
        }

        if (componentClasses.size() > 0) {
            if (includes.isEmpty()) {
                includes.add(".");
            }

            for (final Class<?> clz : componentClasses) {
                if (BaseScheduler.class.isAssignableFrom(clz)) {
                    LOGGER.info("Inject Scheduler Class: {}", clz.getName());

                    final Scheduler scheduler = clz.getAnnotation(Scheduler.class);
                    if (!ObjectCompare.isInListByRegEx(clz.getSimpleName(), includes)
                            || ObjectCompare.isInListByRegEx(clz.getSimpleName(), exclusions)) {
                        LOGGER.warn(": {}, ?? [ {} ]", clz.getSimpleName(), clz.getName());
                        continue;
                    }

                    String parallelProperty = scheduler.parallelProperty();
                    int parallel = 0;
                    String cron = "";
                    for (Properties properties : PropertiesLoader.PROPERTIES.values()) {
                        String value;
                        if (StringUtils.isNotBlank(value = properties.getProperty(parallelProperty))) {
                            /** ? */
                            try {
                                parallel = Integer.parseInt(value);
                            } catch (final NumberFormatException e) {
                                throw new SchedulerException(", ??: [ "
                                        + parallelProperty + " ], : [ " + value + " ]");
                            }
                        }

                        if (StringUtils.isNotBlank(value = properties.getProperty(scheduler.cronProperty()))) {
                            cron = value;
                        }
                    }

                    parallel = scheduler.coreParallel() ? RuntimeUtil.AVAILABLE_PROCESSORS
                            : parallel > 0 ? parallel : scheduler.parallel();
                    if (parallel < 0) {
                        parallel = 0;
                    }

                    if (StringUtils.isBlank(cron)) {
                        cron = scheduler.cron();
                    }

                    for (int p = 0; p < parallel; p++) {
                        final BaseScheduler baseScheduler = (BaseScheduler) Globals.get(Injector.class)
                                .getInstance(clz);
                        final SchedulerConfig conf = new SchedulerConfig();
                        conf.setId(clz.getSimpleName() + '-' + baseScheduler.getIndex(clz.getSimpleName()));
                        conf.setName(DEFAULT_SCHEDULER_NAME_PREFIX + conf.getId());
                        conf.setGroup(clz.getSimpleName());
                        conf.setService(service);
                        conf.setBeforeAfterOnly(scheduler.beforeAfterOnly());
                        conf.setRunNumberOfTimes(scheduler.runNumberOfTimes());
                        conf.setInterval(scheduler.interval());
                        conf.setNum(p);
                        conf.setTotal(parallel);
                        if (StringUtils.isNotBlank(cron)) {
                            try {
                                conf.setCron(new CronExpression(cron));
                            } catch (final ParseException e) {
                                throw new SchedulerException(e.getMessage(), e);
                            }
                        }

                        conf.setDaemon(scheduler.daemon());
                        conf.setLazy(scheduler.lazy());
                        conf.setDefined(scheduler.defined());
                        baseScheduler.setConfig(conf);

                        if (getInstance().stoppedScheduler.containsKey(conf.getId())) {
                            throw new SchedulerException("\n\t??: " + conf.getId()
                                    + ", : {'" + clz.getName() + "', '"
                                    + getInstance().stoppedScheduler.get(conf.getId()).getClass().getName() + "'}");
                        }

                        getInstance().stoppedScheduler.put(conf.getId(), baseScheduler);
                        Set<BaseScheduler> groupScheduler = getInstance().group
                                .get(baseScheduler.getConfig().getGroup());
                        if (groupScheduler == null) {
                            groupScheduler = Sets.newLinkedHashSet();
                        }
                        groupScheduler.add(baseScheduler);
                        getInstance().group.put(conf.getGroup(), groupScheduler);
                    }
                } else {
                    throw new SchedulerException(": [ " + BaseScheduler.class.getName() + " ]");
                }
            }

            /** Create and start Etcd Scheduler */
            createEtcdScheduler(componentClasses);
        }

        IS_LOADED = true;
    }

    private static final void createEtcdScheduler(final Set<Class<?>> componentClasses) {
        try {
            final boolean enable = Boolean.parseBoolean(System.getProperty(ETCD_ENABLE, "false"));
            if (enable) {
                final EtcdScheduler scheduler = new EtcdScheduler(componentClasses);
                ETCD_SCHEDULER = scheduler;
                scheduler.getConfig().getService().execute(scheduler);
                scheduler.syncBaseDirTTL();
                scheduler.syncInfo();
                scheduler.syncClass();

                /** Start Order Scheduler */
                final EtcdOrderWatcherScheduler etcdOrderScheduler = new EtcdOrderWatcherScheduler(
                        scheduler.getEtcd());
                etcdOrderScheduler.getConfig().getService().execute(etcdOrderScheduler);

                if (LocalJmxMonitorScheduler.JMX_ENABLE) {
                    LocalJmxMonitorScheduler jmxScheduler = new LocalJmxMonitorScheduler(scheduler.getEtcd());
                    jmxScheduler.getConfig().getService().execute(jmxScheduler);
                }
            } else {
                ETCD_SCHEDULER = EtcdSchedulerOperate.EMPTY;
            }
        } catch (SchedulerException e) {
            LOGGER.error(e.getMessage(), e);
        }
    }

    public void destory() {
        final long time = System.currentTimeMillis();
        LOGGER.info("?");
        closeAll();
        final List<BaseScheduler> schedulers = Lists.newArrayList();
        schedulers.addAll(getStartedScheduler());
        schedulers.addAll(getStoppingScheduler());
        for (final BaseScheduler scheduler : schedulers) {
            scheduler.thisNotify();
        }

        while ((getStartedSchedulerSize() > 0 || getStoppingSchedulerSize() > 0)
                && System.currentTimeMillis() - time < shutdownTimeout) {
            try {
                Thread.sleep(100L);
            } catch (final InterruptedException e) {
                // ignore
            }

            for (final BaseScheduler scheduler : schedulers) {
                scheduler.thisNotify();
            }
        }

        SchedulerFactory.IS_LOADED = false;
        FACTORY.startedScheduler.clear();
        FACTORY.stoppingScheduler.clear();
        FACTORY.stoppedScheduler.clear();
        group.clear();
        LoggerFactory.getLogger(this.getClass()).info("??, : {}ms",
                System.currentTimeMillis() - time);
    }

    protected class StatusMonitorScheduler extends BaseScheduler {
        private final ConcurrentMap<String, BaseScheduler> closed;

        public StatusMonitorScheduler() {
            final SchedulerConfig conf = new SchedulerConfig();
            conf.setId("StatusMonitorScheduler-0");
            conf.setName("StatusMonitorScheduler");
            conf.setGroup("StatusMonitorScheduler");
            THREAD_FACTORY.setBaseScheduler(this);
            conf.setService((ThreadPoolExecutor) Executors.newFixedThreadPool(1, THREAD_FACTORY));
            conf.setInterval(50L);
            conf.setTotal(1);
            conf.setDaemon(Boolean.TRUE);
            setConfig(conf);
            setClose(false);
            closed = Maps.newConcurrentMap();
        }

        @Override
        public void before() throws SchedulerException {
            stoppingScheduler.forEach((id, scheduler) -> {
                if (scheduler.isClosed()) {
                    closed.put(id, scheduler);
                }
            });
        }

        @Override
        public void execute() throws SchedulerException {
            closed.forEach((id, scheduler) -> {
                if (!scheduler.isRemove()) {
                    stoppedScheduler.put(id, scheduler);
                }

                stoppingScheduler.remove(id, scheduler);

                /** Sync to Etcd by stopped method */
                ETCD_SCHEDULER.stopped(scheduler.getConfig().getGroup(), id, scheduler.isRemove(),
                        scheduler.getAnalysis());
            });
        }

        @Override
        public void after() throws SchedulerException {
            closed.clear();
        }

        @Override
        public void destroy() throws SchedulerException {

        }

    }

    protected class ShutdownHook implements Runnable {
        @Override
        public void run() {
            destory();
        }

    }
}