/* * Copyright (c) 2016 Red Hat, Inc. and/or its affiliates. * * 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: * Cheng Fang - Initial API and implementation */ package org.jberet.schedule; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicInteger; /** * Job scheduler implementation based on {@code java.util.concurrent.ScheduledExecutorService}. * It supports single action and repeatable job schedule, but does not support calendar-based * repeatable job schedule, or persistent job schedule. * * @since 1.3.0 */ public class ExecutorSchedulerImpl extends JobScheduler { /** * Scheduled executor used for job scheduling. */ protected final ScheduledExecutorService executorService; /** * Keeps all job schedules. */ private final ConcurrentMap<String, JobSchedule> schedules; /** * For generating job schedule ids. */ private final AtomicInteger ids = new AtomicInteger(1); /** * Default no-arg constructor. * * @see #ExecutorSchedulerImpl(ConcurrentMap) * @see #ExecutorSchedulerImpl(ConcurrentMap, ScheduledExecutorService) */ public ExecutorSchedulerImpl() { this(null); } /** * Constructs {@code ExecutorSchedulerImpl}, specifying * {@code ConcurrentMap<String, JobSchedule>} for storing all job schedules. * * @param schedules {@code ConcurrentMap<String, JobSchedule>} for storing all job schedules * * @see #ExecutorSchedulerImpl(ConcurrentMap, ScheduledExecutorService) */ public ExecutorSchedulerImpl(final ConcurrentMap<String, JobSchedule> schedules) { this(schedules, null); } /** * Constructs {@code ExecutorSchedulerImpl}, specifying both the * {@code ConcurrentMap<String, JobSchedule>} for storing all job schedules, * and the scheduled executor service. * * @param schedules {@code ConcurrentMap<String, JobSchedule>} for storing all job schedules * @param executorService scheduled executor service */ public ExecutorSchedulerImpl(final ConcurrentMap<String, JobSchedule> schedules, final ScheduledExecutorService executorService) { this.schedules = schedules == null ? new ConcurrentHashMap<String, JobSchedule>() : schedules; this.executorService = executorService == null ? Executors.newSingleThreadScheduledExecutor() : executorService; } @Override public JobSchedule schedule(final JobScheduleConfig scheduleConfig) { final JobSchedule jobSchedule = new JobSchedule(String.valueOf(ids.getAndIncrement()), scheduleConfig); final JobScheduleTask task = new JobScheduleTask(jobSchedule); final Future<?> future; if (scheduleConfig.interval <= 0 && scheduleConfig.afterDelay <= 0) { future = executorService.schedule(task, scheduleConfig.initialDelay, timeUnit); } else if (scheduleConfig.interval > 0) { future = executorService.scheduleAtFixedRate( task, scheduleConfig.initialDelay, scheduleConfig.interval, timeUnit); } else { future = executorService.scheduleWithFixedDelay( task, scheduleConfig.initialDelay, scheduleConfig.afterDelay, timeUnit); } jobSchedule.setFuture(future); schedules.put(jobSchedule.getId(), jobSchedule); return jobSchedule; } @Override public List<JobSchedule> getJobSchedules() { final List<JobSchedule> result = new ArrayList<JobSchedule>(); for (final JobSchedule e : schedules.values()) { final JobSchedule.Status status = e.getStatus(); if (status != JobSchedule.Status.CANCELLED && status != JobSchedule.Status.DONE) { final Future<?> future = e.getFuture(); if (future != null) { if (future.isCancelled()) { e.setStatus(JobSchedule.Status.CANCELLED); } else if (future.isDone()) { e.setStatus(JobSchedule.Status.DONE); } } } result.add(e); } Collections.sort(result, Collections.<JobSchedule>reverseOrder()); return result; } @Override public boolean cancel(final String scheduleId) { boolean result = false; final JobSchedule jobSchedule = schedules.get(scheduleId); if (jobSchedule != null) { final JobSchedule.Status status = jobSchedule.getStatus(); if (status == JobSchedule.Status.DONE || status == JobSchedule.Status.CANCELLED) { return false; } final Future<?> future = jobSchedule.getFuture(); if (future != null) { result = future.cancel(true); if (result) { jobSchedule.setStatus(JobSchedule.Status.CANCELLED); } } } return result; } @Override public JobSchedule getJobSchedule(final String scheduleId) { final JobSchedule jobSchedule = schedules.get(scheduleId); if (jobSchedule != null) { final JobSchedule.Status status = jobSchedule.getStatus(); if (status != JobSchedule.Status.CANCELLED && status != JobSchedule.Status.DONE) { final Future<?> future = jobSchedule.getFuture(); if (future != null) { if (future.isCancelled()) { jobSchedule.setStatus(JobSchedule.Status.CANCELLED); } else if (future.isDone()) { jobSchedule.setStatus(JobSchedule.Status.DONE); } } } } return jobSchedule; } @Override public String toString() { return getClass().getName() + "{executorService=" + executorService + '}'; } }