All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.kagkarlsson.scheduler.SchedulerClient Maven / Gradle / Ivy

There is a newer version: 15.0.0
Show newest version
/*
 * Copyright (C) Gustav Karlsson
 *
 * 

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 com.github.kagkarlsson.scheduler; import static java.util.Optional.ofNullable; import com.github.kagkarlsson.scheduler.exceptions.TaskInstanceCurrentlyExecutingException; import com.github.kagkarlsson.scheduler.exceptions.TaskInstanceNotFoundException; import com.github.kagkarlsson.scheduler.jdbc.AutodetectJdbcCustomization; import com.github.kagkarlsson.scheduler.jdbc.JdbcCustomization; import com.github.kagkarlsson.scheduler.jdbc.JdbcTaskRepository; import com.github.kagkarlsson.scheduler.serializer.Serializer; import com.github.kagkarlsson.scheduler.stats.StatsRegistry; import com.github.kagkarlsson.scheduler.task.Execution; import com.github.kagkarlsson.scheduler.task.SchedulableInstance; import com.github.kagkarlsson.scheduler.task.Task; import com.github.kagkarlsson.scheduler.task.TaskInstance; import com.github.kagkarlsson.scheduler.task.TaskInstanceId; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.function.Consumer; import javax.sql.DataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public interface SchedulerClient { /** * Schedule a new execution if task instance does not already exists. * * @param taskInstance Task-instance, optionally with data * @param executionTime Instant it should run * @see java.time.Instant * @see com.github.kagkarlsson.scheduler.task.TaskInstance * @deprecated use {@link #scheduleIfNotExists(TaskInstance, Instant)} instead. */ @Deprecated void schedule(TaskInstance taskInstance, Instant executionTime); /** * @deprecated use {@link #scheduleIfNotExists(SchedulableInstance)} instead. */ @Deprecated void schedule(SchedulableInstance schedulableInstance); /** * Schedule a new execution if task instance does not already exists. * * @param taskInstance Task-instance, optionally with data * @param executionTime Instant it should run * @see java.time.Instant * @see com.github.kagkarlsson.scheduler.task.TaskInstance * @return true if scheduled successfully */ boolean scheduleIfNotExists(TaskInstance taskInstance, Instant executionTime); /** * Schedule a new execution if task instance does not already exists. * * @param schedulableInstance Task-instance and time it should run * @see com.github.kagkarlsson.scheduler.task.SchedulableInstance * @return true if scheduled successfully */ boolean scheduleIfNotExists(SchedulableInstance schedulableInstance); /** * Update an existing execution to a new execution-time. If the execution does not exist or if it * is currently running, an exception is thrown. * * @param taskInstanceId * @param newExecutionTime the new execution-time * @see java.time.Instant * @see com.github.kagkarlsson.scheduler.task.TaskInstanceId */ void reschedule(TaskInstanceId taskInstanceId, Instant newExecutionTime); /** * Update an existing execution with a new execution-time and new task-data. If the execution does * not exist or if it is currently running, an exception is thrown. * * @param taskInstanceId * @param newExecutionTime the new execution-time * @param newData the new task-data * @see java.time.Instant * @see com.github.kagkarlsson.scheduler.task.TaskInstanceId */ void reschedule(TaskInstanceId taskInstanceId, Instant newExecutionTime, T newData); /** * Update an existing execution with a new execution-time and new task-data. If the execution does * not exist or if it is currently running, an exception is thrown. * * @param schedulableInstance the updated instance */ void reschedule(SchedulableInstance schedulableInstance); /** * Removes/Cancels an execution. * * @param taskInstanceId * @see com.github.kagkarlsson.scheduler.task.TaskInstanceId */ void cancel(TaskInstanceId taskInstanceId); /** * Gets all scheduled executions and supplies them to the provided Consumer. A Consumer is used to * avoid forcing the SchedulerClient to load all executions in memory. Currently running * executions are not returned. * * @param consumer Consumer for the executions */ void fetchScheduledExecutions(Consumer> consumer); void fetchScheduledExecutions( ScheduledExecutionsFilter filter, Consumer> consumer); /** * @see #fetchScheduledExecutions(Consumer) */ default List> getScheduledExecutions() { List> executions = new ArrayList<>(); fetchScheduledExecutions(executions::add); return executions; } /** * @see #fetchScheduledExecutions(Consumer) */ default List> getScheduledExecutions( ScheduledExecutionsFilter filter) { List> executions = new ArrayList<>(); fetchScheduledExecutions(filter, executions::add); return executions; } /** * Gets all scheduled executions for a task and supplies them to the provided Consumer. A Consumer * is used to avoid forcing the SchedulerClient to load all executions in memory. Currently * running executions are not returned. * * @param taskName the name of the task to get scheduled-executions for * @param dataClass the task data-class the data will be serialized and cast to * @param consumer Consumer for the executions */ void fetchScheduledExecutionsForTask( String taskName, Class dataClass, Consumer> consumer); void fetchScheduledExecutionsForTask( String taskName, Class dataClass, ScheduledExecutionsFilter filter, Consumer> consumer); /** * @see #fetchScheduledExecutionsForTask(String, Class, Consumer) */ default List> getScheduledExecutionsForTask(String taskName) { List> executions = new ArrayList<>(); fetchScheduledExecutionsForTask(taskName, Object.class, executions::add); return executions; } /** * @see #fetchScheduledExecutionsForTask(String, Class, Consumer) */ default List> getScheduledExecutionsForTask( String taskName, Class dataClass) { List> executions = new ArrayList<>(); fetchScheduledExecutionsForTask(taskName, dataClass, executions::add); return executions; } /** * @see #fetchScheduledExecutionsForTask(String, Class, Consumer) */ default List> getScheduledExecutionsForTask( String taskName, Class dataClass, ScheduledExecutionsFilter filter) { List> executions = new ArrayList<>(); fetchScheduledExecutionsForTask(taskName, dataClass, filter, executions::add); return executions; } /** * Gets the details for a specific scheduled execution. Currently running executions are also * returned. * * @param taskInstanceId * @return Optional.empty() if no matching execution found * @see com.github.kagkarlsson.scheduler.task.TaskInstanceId * @see com.github.kagkarlsson.scheduler.ScheduledExecution */ Optional> getScheduledExecution(TaskInstanceId taskInstanceId); class Builder { private final DataSource dataSource; private List> knownTasks; private Serializer serializer = Serializer.DEFAULT_JAVA_SERIALIZER; private String tableName = JdbcTaskRepository.DEFAULT_TABLE_NAME; private JdbcCustomization jdbcCustomization; private Builder(DataSource dataSource, List> knownTasks) { this.dataSource = dataSource; this.knownTasks = knownTasks; } public static Builder create(DataSource dataSource, Task... knownTasks) { return new Builder(dataSource, Arrays.asList(knownTasks)); } public static Builder create(DataSource dataSource, List> knownTasks) { return new Builder(dataSource, knownTasks); } public Builder serializer(Serializer serializer) { this.serializer = serializer; return this; } public Builder tableName(String tableName) { this.tableName = tableName; return this; } public Builder jdbcCustomization(JdbcCustomization jdbcCustomization) { this.jdbcCustomization = jdbcCustomization; return this; } public SchedulerClient build() { TaskResolver taskResolver = new TaskResolver(StatsRegistry.NOOP, knownTasks); final SystemClock clock = new SystemClock(); final JdbcCustomization jdbcCustomization = ofNullable(this.jdbcCustomization) .orElseGet(() -> new AutodetectJdbcCustomization(dataSource)); TaskRepository taskRepository = new JdbcTaskRepository( dataSource, false, jdbcCustomization, tableName, taskResolver, new SchedulerClientName(), serializer, clock); return new StandardSchedulerClient(taskRepository, clock); } } class StandardSchedulerClient implements SchedulerClient { private static final Logger LOG = LoggerFactory.getLogger(StandardSchedulerClient.class); protected final TaskRepository taskRepository; private final Clock clock; private SchedulerClientEventListener schedulerClientEventListener; StandardSchedulerClient(TaskRepository taskRepository, Clock clock) { this(taskRepository, SchedulerClientEventListener.NOOP, clock); } StandardSchedulerClient( TaskRepository taskRepository, SchedulerClientEventListener schedulerClientEventListener, Clock clock) { this.taskRepository = taskRepository; this.schedulerClientEventListener = schedulerClientEventListener; this.clock = clock; } @Override public void schedule(TaskInstance taskInstance, Instant executionTime) { // ignore result even if failed to schedule due to duplicates for backwards-compatibility scheduleIfNotExists(taskInstance, executionTime); } @Override public boolean scheduleIfNotExists(TaskInstance taskInstance, Instant executionTime) { boolean success = taskRepository.createIfNotExists(SchedulableInstance.of(taskInstance, executionTime)); if (success) { notifyListeners(ClientEvent.EventType.SCHEDULE, taskInstance, executionTime); } return success; } @Override public boolean scheduleIfNotExists(SchedulableInstance schedulableInstance) { return scheduleIfNotExists( schedulableInstance.getTaskInstance(), schedulableInstance.getNextExecutionTime(clock.now())); } @Override public void schedule(SchedulableInstance schedulableInstance) { schedule( schedulableInstance.getTaskInstance(), schedulableInstance.getNextExecutionTime(clock.now())); } @Override public void reschedule(TaskInstanceId taskInstanceId, Instant newExecutionTime) { reschedule(taskInstanceId, newExecutionTime, null); } @Override public void reschedule(SchedulableInstance schedulableInstance) { reschedule( schedulableInstance, schedulableInstance.getNextExecutionTime(clock.now()), schedulableInstance.getTaskInstance().getData()); } @Override public void reschedule(TaskInstanceId taskInstanceId, Instant newExecutionTime, T newData) { String taskName = taskInstanceId.getTaskName(); String instanceId = taskInstanceId.getId(); Optional execution = taskRepository.getExecution(taskName, instanceId); if (execution.isPresent()) { if (execution.get().isPicked()) { throw new TaskInstanceCurrentlyExecutingException(taskName, instanceId); } boolean success; if (newData == null) { success = taskRepository.reschedule(execution.get(), newExecutionTime, null, null, 0); } else { success = taskRepository.reschedule(execution.get(), newExecutionTime, newData, null, null, 0); } if (success) { notifyListeners(ClientEvent.EventType.RESCHEDULE, taskInstanceId, newExecutionTime); } } else { throw new TaskInstanceNotFoundException(taskName, instanceId); } } @Override public void cancel(TaskInstanceId taskInstanceId) { String taskName = taskInstanceId.getTaskName(); String instanceId = taskInstanceId.getId(); Optional execution = taskRepository.getExecution(taskName, instanceId); if (execution.isPresent()) { if (execution.get().isPicked()) { throw new TaskInstanceCurrentlyExecutingException(taskName, instanceId); } taskRepository.remove(execution.get()); notifyListeners( ClientEvent.EventType.CANCEL, taskInstanceId, execution.get().executionTime); } else { throw new TaskInstanceNotFoundException(taskName, instanceId); } } @Override public void fetchScheduledExecutions(Consumer> consumer) { fetchScheduledExecutions(ScheduledExecutionsFilter.all().withPicked(false), consumer); } @Override public void fetchScheduledExecutions( ScheduledExecutionsFilter filter, Consumer> consumer) { taskRepository.getScheduledExecutions( filter, execution -> consumer.accept(new ScheduledExecution<>(Object.class, execution))); } @Override public void fetchScheduledExecutionsForTask( String taskName, Class dataClass, Consumer> consumer) { fetchScheduledExecutionsForTask( taskName, dataClass, ScheduledExecutionsFilter.all().withPicked(false), consumer); } @Override public void fetchScheduledExecutionsForTask( String taskName, Class dataClass, ScheduledExecutionsFilter filter, Consumer> consumer) { taskRepository.getScheduledExecutions( filter, taskName, execution -> consumer.accept(new ScheduledExecution<>(dataClass, execution))); } @Override public Optional> getScheduledExecution( TaskInstanceId taskInstanceId) { Optional e = taskRepository.getExecution(taskInstanceId.getTaskName(), taskInstanceId.getId()); return e.map(oe -> new ScheduledExecution<>(Object.class, oe)); } private void notifyListeners( ClientEvent.EventType eventType, TaskInstanceId taskInstanceId, Instant executionTime) { try { schedulerClientEventListener.newEvent( new ClientEvent( new ClientEvent.ClientEventContext(eventType, taskInstanceId, executionTime))); } catch (Exception e) { LOG.error("Error when notifying SchedulerClientEventListener.", e); } } } class SchedulerClientName implements SchedulerName { @Override public String getName() { return "SchedulerClient"; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy