/******************************************************************************* * Copyright (c) 2012-2017 Codenvy, S.A. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.commons.schedule.executor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.CancellationException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; /** * Scheduled thread-pool executor implementation that leverages a CronExpression * to calculate future execution times for scheduled tasks. */ public class CronThreadPoolExecutor extends ScheduledThreadPoolExecutor implements CronExecutorService { private static final Logger LOG = LoggerFactory.getLogger(CronThreadPoolExecutor.class); private final List<CountDownLatch> cronJobWatchDogs; /** * Constructs a new CronThreadPoolExecutor. * * @param corePoolSize * the pool size */ public CronThreadPoolExecutor(int corePoolSize) { super(corePoolSize); this.cronJobWatchDogs = new ArrayList<>(); this.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); } /** * Constructs a new CronThreadPoolExecutor. * * @param corePoolSize * the pool size * @param threadFactory * the thread factory */ public CronThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) { super(corePoolSize, threadFactory); this.cronJobWatchDogs = new ArrayList<>(); this.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); } /** * Constructs a new CronThreadPoolExecutor. * * @param corePoolSize * the pool size * @param handler * the handler for rejected executions */ public CronThreadPoolExecutor(int corePoolSize, RejectedExecutionHandler handler) { super(corePoolSize, handler); this.cronJobWatchDogs = new ArrayList<>(); this.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); } /** * Constructs a new CronThreadPoolExecutor. * * @param corePoolSize * the pool size * @param handler * the handler for rejecting executions * @param threadFactory * the thread factory */ public CronThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory, RejectedExecutionHandler handler) { super(corePoolSize, threadFactory, handler); this.cronJobWatchDogs = new ArrayList<>(); } @Override public Future<?> schedule(final Runnable task, final CronExpression expression) { if (task == null) { throw new NullPointerException(); } setCorePoolSize(getCorePoolSize() + 1); Runnable scheduleTask = new Runnable() { @Override public void run() { CountDownLatch countDownLatch = new CountDownLatch(1); cronJobWatchDogs.add(countDownLatch); Date now = new Date(); Date time = expression.getNextValidTimeAfter(now); try { while (time != null) { CronThreadPoolExecutor.this .schedule(task, time.getTime() - now.getTime(), TimeUnit.MILLISECONDS); while (now.before(time)) { LOG.debug("Cron watch dog wait {} ", time.getTime() - now.getTime()); if (countDownLatch.await(time.getTime() - now.getTime(), TimeUnit.MILLISECONDS)) { LOG.debug("Stopping cron watch dog."); return; } now = new Date(); } time = expression.getNextValidTimeAfter(now); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } catch (RejectedExecutionException | CancellationException e) { LOG.error(e.getMessage(), e); } } }; return this.submit(scheduleTask); } @Override public void shutdown() { for (CountDownLatch cronJobWatchDog : cronJobWatchDogs) { cronJobWatchDog.countDown(); } cronJobWatchDogs.clear(); super.shutdown(); LOG.debug("Active {} Pool {}, CEPTAS {} , EEDTAS {} , Task count {} , queue size {}", getActiveCount(), getPoolSize(), getContinueExistingPeriodicTasksAfterShutdownPolicy(), getExecuteExistingDelayedTasksAfterShutdownPolicy(), getTaskCount(), getQueue().size() ); } @Override public List<Runnable> shutdownNow() { for (CountDownLatch cronJobWatchDog : cronJobWatchDogs) { cronJobWatchDog.countDown(); } cronJobWatchDogs.clear(); LOG.debug("Active {} Pool {}, CEPTAS {} , EEDTAS {} , Task count {} , queue size {}", getActiveCount(), getPoolSize(), getContinueExistingPeriodicTasksAfterShutdownPolicy(), getExecuteExistingDelayedTasksAfterShutdownPolicy(), getTaskCount(), getQueue().size() ); return super.shutdownNow(); } }