/*
* Copyright (c) 2010-2013 Evolveum
*
* 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.evolveum.midpoint.task.api;
import java.text.ParseException;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.schema.GetOperationOptions;
import com.evolveum.midpoint.schema.ResultHandler;
import com.evolveum.midpoint.schema.SearchResultList;
import com.evolveum.midpoint.schema.SearchResultMetadata;
import com.evolveum.midpoint.schema.SelectorOptions;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException;
import com.evolveum.midpoint.util.exception.ObjectNotFoundException;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.xml.ns._public.common.common_3.CleanupPolicyType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.NodeType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType;
import org.jetbrains.annotations.NotNull;
/**
* <p>Task Manager Interface.</p>
* <p>
* Status: public
* Stability: DRAFT
* @version 0.1
* @author Radovan Semancik
* @author Pavol Mederly
* </p>
* <p>
* Task manager provides controls task execution, coordination, distribution and failover between nodes, etc.
* </p><p>
* This interface is just a basic framework for task management now. Although we hope that this is roughly almost final
* shape of the interface, the implementation is not complete and some changes may happen.
* </p>
* <p>
* This definition specifies interface of Task Manager - a component that controls (asynchronous) task execution.
* </p><p>
* The task manager can store the task for later execution, switch them to background
* resume execution of a task from a different node, etc. Generally speaking, task
* manager provides operation to manage tasks in the whole midPoint cluster of IDM nodes.
* </p><p>
* This interface partially adheres to [Common Interface Concepts], but the goals are slightly
* different. This interface should be conveniently used also for tasks that are not persistent
* (synchronous short tasks). Therefore some methods are made much more user-friendly while
* tolerating some redundancy in the interface.
* </p>
*/
public interface TaskManager {
long WAIT_INDEFINITELY = 0L;
long DO_NOT_WAIT = -1L;
long DO_NOT_STOP = -2L;
//region Generic operations delegated from the model
/**
*
* @param type
* @param query
* @param options
* @param parentResult
* @param <T>
* @return
* @throws SchemaException
*
* Notes: Implemented options are:
*
* - noFetch: it causes task manager NOT to ask remote nodes about node/task status.
* - (for tasks) TaskType.F_NEXT_RUN_START_TIMESTAMP: it can be used to disable asking Quartz for next run start time
* - other options that are passed down to repository
*/
<T extends ObjectType> SearchResultList<PrismObject<T>> searchObjects(Class<T> type, ObjectQuery query, Collection<SelectorOptions<GetOperationOptions>> options, OperationResult parentResult) throws SchemaException;
<T extends ObjectType> SearchResultMetadata searchObjectsIterative(Class<T> type, ObjectQuery query, Collection<SelectorOptions<GetOperationOptions>> options, ResultHandler<T> handler, OperationResult parentResult) throws SchemaException;
/**
* Counts the number of objects.
*
* @param type
* @param query
* @param parentResult
* @param <T>
* @return
* @throws SchemaException
*/
<T extends ObjectType> int countObjects(Class<T> type, ObjectQuery query, OperationResult parentResult) throws SchemaException;
void waitForTransientChildren(Task task, OperationResult result);
/**
* TODO
*
* @param clazz
* @param oid
* @param options
* @param task
* @param result
* @param <T>
* @return
*/
<T extends ObjectType> PrismObject<T> getObject(Class<T> clazz, String oid, Collection<SelectorOptions<GetOperationOptions>> options, OperationResult result) throws ObjectNotFoundException, SchemaException;
/**
* Add new task.
*
* The OID provided in the task may be empty. In that case the OID
* will be assigned by the implementation of this method and it will be
* provided as return value.
*
* This operation should fail if such object already exists (if object with
* the provided OID already exists).
*
* The operation may fail if provided OID is in an unusable format for the
* storage. Generating own OIDs and providing them to this method is not
* recommended for normal operation.
*
* Should be atomic. Should not allow creation of two objects with the same
* OID (even if created in parallel).
*
* The operation may fail if the object to be created does not conform to
* the underlying schema of the storage system or the schema enforced by the
* implementation.
*
* @param taskPrism
* object to create
* @param parentResult
* parent OperationResult (in/out)
* @return OID assigned to the created object
*
* @throws ObjectAlreadyExistsException
* object with specified identifiers already exists, cannot add
* @throws SchemaException
* error dealing with storage schema, e.g. schema violation
* @throws IllegalArgumentException
* wrong OID format, etc.
*/
public String addTask(PrismObject<TaskType> taskPrism, OperationResult parentResult)
throws ObjectAlreadyExistsException, SchemaException;
/**
* Modifies task using relative change description. Must fail if object with
* provided OID does not exists. Must fail if any of the described changes
* cannot be applied. Should be atomic.
*
* If two or more modify operations are executed in parallel, the operations
* should be merged. In case that the operations are in conflict (e.g. one
* operation adding a value and the other removing the same value), the
* result is not deterministic.
*
* The operation may fail if the modified object does not conform to the
* underlying schema of the storage system or the schema enforced by the
* implementation.
*
* HOWEVER, the preferred way of modifying tasks is to use methods in Task interface.
*
* @param oid
* OID of the task to be changed
* @param modifications
* specification of object changes
* @param parentResult
* parent OperationResult (in/out)
*
* @throws ObjectNotFoundException
* specified object does not exist
* @throws SchemaException
* resulting object would violate the schema
* @throws IllegalArgumentException
* wrong OID format, described change is not applicable
*/
public void modifyTask(String oid, Collection<? extends ItemDelta> modifications, OperationResult parentResult)
throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException;
/**
* Deletes task with provided OID. Must fail if object with specified OID
* does not exists. Should be atomic.
*
* BEWARE: call this method only if you are pretty sure the task is not running.
* Otherwise the running thread will complain when it will try to store task result into repo.
* (I.e. it is a good practice to suspend the task before deleting.)
*
* @param oid
* OID of object to delete
* @param parentResult
* parent OperationResult (in/out)
*
* @throws ObjectNotFoundException
* specified object does not exist
* @throws IllegalArgumentException
* wrong OID format, described change is not applicable
*/
public void deleteTask(String oid, OperationResult parentResult) throws ObjectNotFoundException, SchemaException;
//endregion
//region Basic working with tasks (create, get, modify, delete)
// ==================================================== Basic working with tasks (create, get, modify, delete)
/**
* Creates new transient, running task instance.
*
* This is fact creates usual "synchronous" task.
*
* This is useful for normal day-to-day tasks that are either
* synchronous or start as a synchronous and are switched to
* asynchronous task later.
*
* @return transient, running task instance
*/
public Task createTaskInstance();
/**
* Creates task instance from the XML task representation.
*
* @param taskPrism JAXB (XML) representation of the task
* @return new Java representation of the task
* @throws SchemaException The provided taskType is not compliant to schema
*/
@NotNull
Task createTaskInstance(PrismObject<TaskType> taskPrism, OperationResult parentResult) throws SchemaException;
/**
* Creates new transient, running task instance.
*
* This is fact creates usual "synchronous" task.
*
* This is useful for normal day-to-day tasks that are either
* synchronous or start as a synchronous and are switched to
* asynchronous task later.
*
* The result inside the task will be initialized with
* specified operation name.
*
* @param operationName operation name to use as a root for new result in task
* @return new Java representation of the task
*/
public Task createTaskInstance(String operationName);
/**
* Creates task instance from the XML task representation.
*
* If there is not a result inside the task, it will create the
* result with specified operation name.
*
* @param taskPrism Prism representation of the task
* @param operationName operation name to use as a root for new result in task
* @return new Java representation of the task
* @throws SchemaException The provided taskType is not compliant to schema
*/
@NotNull
public Task createTaskInstance(PrismObject<TaskType> taskPrism, String operationName, OperationResult parentResult) throws SchemaException;
/**
* Returns a task with specified OID.
*
* This operation will look up a task instance in the repository and return it in a form of Task object.
*
* Works only on persistent tasks.
*
* @param taskOid OID of the persistent task.
* @return Task instance
* @throws SchemaException error dealing with resource schema
* @throws ObjectNotFoundException wrong OID format, etc.
*/
@NotNull
Task getTask(String taskOid, OperationResult parentResult) throws ObjectNotFoundException, SchemaException;
/**
* Returns a task with a given identifier.
*
* (NOTE: Currently finds only persistent tasks. In the future, we plan to support searching for transient tasks as well.)
*
* @param identifier task identifier to search for
* @param parentResult
* @return
* @throws SchemaException
* @throws ObjectNotFoundException
*/
Task getTaskByIdentifier(String identifier, OperationResult parentResult) throws SchemaException, ObjectNotFoundException;
/**
* TODO
*
* @param identifier
* @param options
* @param parentResult
* @return
*/
PrismObject<TaskType> getTaskTypeByIdentifier(String identifier, Collection<SelectorOptions<GetOperationOptions>> options, OperationResult parentResult) throws SchemaException, ObjectNotFoundException;
/**
* Deletes obsolete tasks, as specified in the policy.
*
* This method removes whole task trees, i.e. not single tasks. A task tree is deleted if the root task is closed
* (assuming all tasks in the tree are closed) and was closed before at least specified time.
*
*
* @param closedTasksPolicy specifies which tasks are to be deleted, e.g. how old they have to be
* @param task task, within which context the cleanup executes (used to test for interruptions)
* @param opResult
* @throws SchemaException
*/
void cleanupTasks(CleanupPolicyType closedTasksPolicy, Task task, OperationResult opResult) throws SchemaException;
/**
* This is a signal to task manager that a new task was created in the repository.
* Task manager can react to it e.g. by creating shadow quartz job and trigger.
*
* @param oid
*/
void onTaskCreate(String oid, OperationResult parentResult);
/**
* This is a signal to task manager that a task was removed from the repository.
* Task manager can react to it e.g. by removing shadow quartz job and trigger.
*
* @param oid
*/
void onTaskDelete(String oid, OperationResult parentResult);
//endregion
//region Searching for tasks
// ==================================================== Searching for tasks
/**
* Returns tasks satisfying given query.
*
* Comparing to searchObjects(TaskType) in repo, there are the following differences:
* (1) This method combines information from the repository with run-time information obtained from cluster nodes
* (clusterStatusInformation), mainly to tell what tasks are really executing at this moment. The repository
* contains 'node' attribute (telling on which node task runs), which may be out-of-date for nodes which
* crashed recently.
* (2) this method returns Tasks, not TaskTypes - a Task provides some information (e.g. getNextRunStartTime())
* that is not stored in repository; Task object can be directly used as an input to several methods,
* like suspendTask() or releaseTask().
*
* However, the reason (2) is only of a technical character. So, if necessary, this method can be changed
* to return a list of TaskTypes instead of Tasks.
*
* @param query Search query
* @param clusterStatusInformation If null, the method will query cluster nodes to get up-to-date runtime information.
* If non-null, the method will use the provided information. Used to optimize
* network traffic in case of repeating calls to searchTasks/searchNodes (e.g. when
* displaying them on one page).
* @param result
* @return
* @throws SchemaException
*/
//List<Task> searchTasks(ObjectQuery query, ClusterStatusInformation clusterStatusInformation, OperationResult result) throws SchemaException;
/**
* Returns the number of tasks satisfying given query.
*
* @param query search query
* @param result
* @return
* @throws SchemaException
*/
// int countTasks(ObjectQuery query, OperationResult result) throws SchemaException;
/**
* Returns tasks that currently run on this node.
* E.g. tasks that have allocated threads.
*
* Does not look primarily into repository, but looks at runtime structures describing the task execution.
*
* @return tasks that currently run on this node.
*/
public Set<Task> getLocallyRunningTasks(OperationResult parentResult) throws TaskManagerException;
/**
* Returns locally-run task by identifier. Returned instance is the same as is being used to carrying out
* operations. SO USE WITH CARE.
*
* EXPERIMENTAL. Should be replaced by something like "get operational information".
*
* @param lightweightIdentifier
* @param parentResult
* @return
* @throws TaskManagerException
*/
public Task getLocallyRunningTaskByIdentifier(String lightweightIdentifier);
//endregion
//region Suspending, resuming and scheduling the tasks
// ==================================================== Suspending and resuming the tasks
/**
* Suspends a set of tasks. Sets their execution status to SUSPENDED. Stops their execution (unless doNotStop is set).
*
* @param taskOids a collection of OIDs of tasks that have to be suspended
* @param waitTime how long (in milliseconds) to wait for stopping the execution of tasks;
* WAIT_INDEFINITELY means wait indefinitely :)
* DO_NOT_WAIT means stop the tasks, but do not wait for finishing their execution
* DO_NOT_STOP means do not try to stop the task execution. Tasks will only be put into SUSPENDED state, and
* their executions (if any) will be left as they are. Use this option only when you know what you're doing.
* @param parentResult
* @return true if all the tasks were stopped, false if some tasks continue to run or if stopping was not requested (DO_NOT_STOP option)
*/
boolean suspendTasks(Collection<String> taskOids, long waitForStop, OperationResult parentResult);
/**
* Suspend a task. The same as above - stops one-member set.
*/
boolean suspendTask(Task task, long waitTime, OperationResult parentResult);
/**
* Suspends tasks and deletes them.
*
* @param taskOidList List of task OIDs to be suspended and deleted.
* @param suspendTimeout How long (in milliseconds) to wait for task suspension before proceeding with deletion.
* @param alsoSubtasks Should also subtasks be deleted?
* @param parentResult
*/
void suspendAndDeleteTasks(Collection<String> taskOidList, long suspendTimeout, boolean alsoSubtasks, OperationResult parentResult);
/**
* Resume suspended task.
*
* @param task task instance to be resumed.
* @throws SchemaException
* @throws ObjectNotFoundException
*/
public void resumeTask(Task task, OperationResult parentResult) throws ObjectNotFoundException, SchemaException;
/**
* Resume suspended tasks.
*
* @param taskOids a collection of OIDs of tasks that have to be resumed
* @throws SchemaException
* @throws com.evolveum.midpoint.util.exception.ObjectNotFoundException
*/
void resumeTasks(Collection<String> taskOids, OperationResult parentResult);
/**
* Puts a runnable/running task into WAITING state.
*
* @param task a runnable/running task
* @param reason the reason for waiting, which is stored into the repository
* @param parentResult
* @throws ObjectNotFoundException
* @throws SchemaException
*/
void pauseTask(Task task, TaskWaitingReason reason, OperationResult parentResult) throws ObjectNotFoundException, SchemaException;
/**
* Puts a WAITING task back into RUNNABLE state.
*
* @param task
* @param parentResult
* @throws ObjectNotFoundException
* @throws SchemaException
*/
void unpauseTask(Task task, OperationResult parentResult) throws ObjectNotFoundException, SchemaException;
/**
* Switches the provided task to background, making it asynchronous.
*
* The provided task will be "released" to other nodes to execute. There is no guarantee that
* the task will execute on the same node that called the switchToBackground() method.
*
* @param task task to switch to background.
*/
public void switchToBackground(Task task, OperationResult parentResult);
/**
* Schedules a RUNNABLE task or CLOSED single-run task to be run immediately. (If the task will really start immediately,
* depends e.g. on whether a scheduler is started, whether there are available threads, and so on.)
*
* @param task
* @param parentResult
*/
void scheduleTaskNow(Task task, OperationResult parentResult) throws SchemaException, ObjectNotFoundException;
/**
* Schedules a RUNNABLE/CLOSED tasks to be run immediately. (If a task will really start immediately,
* depends e.g. on whether a scheduler is started, whether there are available threads, and so on.)
*
* @param taskOids a collection of OIDs of tasks that have to be scheduled
* @param parentResult
*/
void scheduleTasksNow(Collection<String> taskOids, OperationResult parentResult);
//endregion
//region Working with nodes (searching, mananging)
// ==================================================== Working with nodes (searching, mananging)
/**
* Returns the number of nodes satisfying given query.
*
* @param query search query
* @param result
* @return
* @throws SchemaException
*/
// int countNodes(ObjectQuery query, OperationResult result) throws SchemaException;
/**
* Returns identifier for current node.
* @return
*/
String getNodeId();
/**
* Checks whether supplied node is the current node.
*
* @param node
* @return true if node is the current node
*/
boolean isCurrentNode(PrismObject<NodeType> node);
/**
* Deletes a node from the repository.
* (Checks whether the node is not up before deleting it.)
*
* @param nodeOid
* @param result
*/
void deleteNode(String nodeOid, OperationResult result) throws SchemaException, ObjectNotFoundException;
//endregion
//region Managing state of the node(s)
// ==================================================== Managing state of the node(s)
/**
* Shuts down current node. Stops all tasks and cluster manager thread as well.
* Waits until all tasks on this node finish.
*/
void shutdown();
/**
* Deactivates service threads (temporarily).
*
* This will suspend all background activity such as scanning threads, heartbeats and similar mechanisms.
*
* Note: The threads are normally activated after task manager implementation starts. This methods should not be used
* in a normal case.
*
* WARNING: this feature is intended for development-time diagnostics and should not be used on production environments.
* Suspending the threads may affect correct behavior of the system (such as timeouts on heartbeats). Use this feature
* only if you really know what you are doing.
*
* timeToWait is only for orientation = it may be so that the implementation would wait 2 or 3 times this value
* (if it waits separately for several threads completion)
*/
boolean deactivateServiceThreads(long timeToWait, OperationResult parentResult);
/**
* Re-activates the service threads after they have been deactivated.
*/
void reactivateServiceThreads(OperationResult parentResult);
/**
* Returns true if the service threads are running.
*
* This method returns true in a normal case. It returns false is the threads were temporarily suspended.
*
* @return true if the service threads are running.
*/
boolean getServiceThreadsActivationState();
/**
* Stops the scheduler on a given node. This means that at that node no tasks will be started.
*
* @param nodeIdentifier Node on which the scheduler should be stopped. Null means current node.
*/
void stopScheduler(String nodeIdentifier, OperationResult parentResult);
void stopSchedulers(Collection<String> nodeIdentifiers, OperationResult parentResult);
/**
* Stops a set of schedulers (on their nodes) and tasks that are executing on these nodes.
*
* @param nodeIdentifiers collection of node identifiers
* @param waitTime how long to wait for task shutdown, in milliseconds
* 0 = indefinitely
* -1 = do not wait at all
* @param parentResult
* @return
*/
boolean stopSchedulersAndTasks(Collection<String> nodeIdentifiers, long waitTime, OperationResult parentResult);
/**
* Starts the scheduler on a given node. A prerequisite is that the node is running and its
* TaskManager is not in an error state.
*
* @param nodeIdentifier Node on which the scheduler should be started. Null means current node.
* @return true if the operation succeeded; false otherwise.
*/
void startScheduler(String nodeIdentifier, OperationResult parentResult);
void startSchedulers(Collection<String> nodeIdentifiers, OperationResult parentResult);
//endregion
//region Notifications
/**
* Registers a task listener that will be notified on task-related events.
*
* @param taskListener listener to be registered
*/
void registerTaskListener(TaskListener taskListener);
/**
* Unregisters a task listener.
*
* @param taskListener listener to be unregisteted
*/
void unregisterTaskListener(TaskListener taskListener);
//endregion
//region Miscellaneous methods
// ==================================================== Miscellaneous methods
/**
* Post initialization, e.g. starts the actual scheduling of tasks on this node.
*/
void postInit(OperationResult result);
/**
* Synchronizes information in midPoint repository and task scheduling database.
*
* @param parentResult
*/
void synchronizeTasks(OperationResult parentResult);
/**
* Gets next scheduled execution time for a given task.
*
* @param oid OID of the task
* @param result
* @return null if there's no next scheduled execution for a given task or if a task with given OID does not exist
*/
Long getNextRunStartTime(String oid, OperationResult result);
/**
* Gets a list of all task categories.
*
* @return
*/
List<String> getAllTaskCategories();
/**
* Returns a default handler URI for a given task category.
*
* @param category
* @return
*/
String getHandlerUriForCategory(String category);
/**
* Validates a cron expression for scheduling tasks - without context of any given task.
* @param cron expression to validate
* @return an exception if there's something wrong with the expression (null if it's OK).
*/
ParseException validateCronExpression(String cron);
/**
* Registers a handler for a specified handler URI.
*
* @param uri URI of the handler, e.g. http://midpoint.evolveum.com/xml/ns/public/model/cleanup/handler-3
* @param handler instance of the handler
*/
void registerHandler(String uri, TaskHandler handler);
void registerTaskDeletionListener(TaskDeletionListener listener);
//endregion
}