/*
* ProActive Parallel Suite(TM):
* The Open Source library for parallel and distributed
* Workflows & Scheduling, Orchestration, Cloud Automation
* and Big Data Analysis on Enterprise Grids & Clouds.
*
* Copyright (c) 2007 - 2017 ActiveEon
* Contact: contact@activeeon.com
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation: version 3 of
* the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* If needed, contact us to obtain a release under GPL Version 2 or 3
* or a different license than the AGPL.
*/
package org.ow2.proactive.scheduler.job;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.xml.bind.annotation.XmlTransient;
import org.apache.log4j.Logger;
import org.objectweb.proactive.extensions.dataspaces.core.naming.NamingService;
import org.ow2.proactive.authentication.crypto.Credentials;
import org.ow2.proactive.scheduler.common.NotificationData;
import org.ow2.proactive.scheduler.common.SchedulerEvent;
import org.ow2.proactive.scheduler.common.exception.ExecutableCreationException;
import org.ow2.proactive.scheduler.common.exception.UnknownTaskException;
import org.ow2.proactive.scheduler.common.job.Job;
import org.ow2.proactive.scheduler.common.job.JobId;
import org.ow2.proactive.scheduler.common.job.JobInfo;
import org.ow2.proactive.scheduler.common.job.JobPriority;
import org.ow2.proactive.scheduler.common.job.JobState;
import org.ow2.proactive.scheduler.common.job.JobStatus;
import org.ow2.proactive.scheduler.common.task.OnTaskError;
import org.ow2.proactive.scheduler.common.task.TaskId;
import org.ow2.proactive.scheduler.common.task.TaskInfo;
import org.ow2.proactive.scheduler.common.task.TaskState;
import org.ow2.proactive.scheduler.common.task.TaskStatus;
import org.ow2.proactive.scheduler.common.task.flow.FlowAction;
import org.ow2.proactive.scheduler.common.task.flow.FlowActionType;
import org.ow2.proactive.scheduler.core.SchedulerStateUpdate;
import org.ow2.proactive.scheduler.core.properties.PASchedulerProperties;
import org.ow2.proactive.scheduler.descriptor.JobDescriptor;
import org.ow2.proactive.scheduler.descriptor.JobDescriptorImpl;
import org.ow2.proactive.scheduler.descriptor.TaskDescriptor;
import org.ow2.proactive.scheduler.job.termination.handlers.TerminateIfTaskHandler;
import org.ow2.proactive.scheduler.job.termination.handlers.TerminateLoopHandler;
import org.ow2.proactive.scheduler.job.termination.handlers.TerminateReplicateTaskHandler;
import org.ow2.proactive.scheduler.task.TaskIdImpl;
import org.ow2.proactive.scheduler.task.TaskInfoImpl;
import org.ow2.proactive.scheduler.task.TaskResultImpl;
import org.ow2.proactive.scheduler.task.internal.InternalTask;
import it.sauronsoftware.cron4j.Predictor;
/**
* Internal and global description of a job. This class contains all information
* about the job to launch. It also provides method to manage the content
* regarding the scheduling process. Specific internal job may extend this
* abstract class.
*
* @author The ProActive Team
* @since ProActive Scheduling 0.9
*/
public abstract class InternalJob extends JobState {
protected static final Logger LOGGER = Logger.getLogger(InternalJob.class);
/** List of every tasks in this job. */
protected Map<TaskId, InternalTask> tasks = new HashMap<>();
/** Informations (that can be modified) about job execution */
protected JobInfoImpl jobInfo = new JobInfoImpl();
/** Job descriptor for dependences management */
@XmlTransient
private JobDescriptor jobDescriptor;
/** DataSpace application manager per task. The key is the task ID */
// Not DB managed, created once needed.
@XmlTransient
private Map<Long, TaskDataSpaceApplication> taskDataSpaceApplications;
/** Initial waiting time for a task before restarting in millisecond */
private long restartWaitingTimer = PASchedulerProperties.REEXECUTION_INITIAL_WAITING_TIME.getValueAsInt();
/**
* used credentials to fork as user id. Can be null, or contain
* user/pwd[/key]
*/
@XmlTransient
private Credentials credentials = null;
@XmlTransient
private final transient TerminateLoopHandler terminateLoopHandler;
@XmlTransient
private final transient TerminateIfTaskHandler terminateIfTaskHandler;
@XmlTransient
private final transient TerminateReplicateTaskHandler terminateReplicateTaskHandler;
@XmlTransient
private final transient Set<TaskId> faultyTasks;
@XmlTransient
private Job taskFlowJob;
/** Hibernate default constructor */
public InternalJob() {
this.faultyTasks = new HashSet<>();
this.terminateLoopHandler = new TerminateLoopHandler(this);
this.terminateIfTaskHandler = new TerminateIfTaskHandler(this);
this.terminateReplicateTaskHandler = new TerminateReplicateTaskHandler(this);
}
/**
* Create a new Job with the given parameters. It provides methods to add or
* remove tasks.
*
* @param name
* the current job name.
* @param priority
* the priority of this job between 1 and 5.
* @param onTaskError
* Sets the error behavior if a task fails.
* @param description
* a short description of the job and what it will do.
*/
public InternalJob(String name, JobPriority priority, OnTaskError onTaskError, String description) {
this();
this.name = name;
this.jobInfo.setPriority(priority);
this.setOnTaskError(onTaskError);
this.description = description;
}
/**
* This method will perform two actions. First, it will update the job with
* the information contained in the givem taskInfo. Then, it will update the
* proper task using the same given taskInfo.
*
* @param info
* a taskInfo containing new information about the task.
*/
@Override
public synchronized void update(TaskInfo info) {
// ensure that is a JobInfoImpl
// if not, we are in client side and client brings its own JobInfo
// Implementation
if (!getId().equals(info.getJobId())) {
throw new IllegalArgumentException("This job info is not applicable for this job. (expected id is '" +
getId() + "' but was '" + info.getJobId() + "'");
}
jobInfo = (JobInfoImpl) info.getJobInfo();
try {
tasks.get(info.getTaskId()).update(info);
} catch (NullPointerException e) {
throw new IllegalArgumentException("This task info is not applicable in this job. (task id '" +
info.getTaskId() + "' not found)");
}
}
@Override
public synchronized void update(JobInfo info) {
if (!getId().equals(info.getJobId())) {
throw new IllegalArgumentException("This job info is not applicable for this job. (expected id is '" +
getId() + "' but was '" + info.getJobId() + "'");
}
// update job info
this.jobInfo = (JobInfoImpl) info;
// update skipped tasks
if (this.jobInfo.getTasksSkipped() != null) {
for (TaskId id : tasks.keySet()) {
if (this.jobInfo.getTasksSkipped().contains(id)) {
TaskInfoImpl taskInfo = (TaskInfoImpl) tasks.get(id).getTaskInfo();
taskInfo.setStatus(TaskStatus.SKIPPED);
}
}
}
}
/**
* @see org.ow2.proactive.scheduler.common.job.JobState#getJobInfo()
*/
@Override
public JobInfo getJobInfo() {
return jobInfo;
}
public void setJobInfo(JobInfoImpl jobInfo) {
this.jobInfo = jobInfo;
}
/**
* Append a task to this job.
*
* @param task
* the task to add.
* @return true if the task has been correctly added to the job, false if
* not.
*/
public boolean addTask(InternalTask task) {
int taskId = tasks.size();
task.setId(TaskIdImpl.createTaskId(getId(), task.getName(), taskId));
boolean result = (tasks.put(task.getId(), task) == null);
if (result) {
jobInfo.setTotalNumberOfTasks(jobInfo.getTotalNumberOfTasks() + 1);
}
return result;
}
/**
* Start a new task will set some count and update dependencies if
* necessary.
*
* @param td
* the task which has just been started.
*/
public void startTask(InternalTask td) {
setNumberOfPendingTasks(getNumberOfPendingTasks() - 1);
setNumberOfRunningTasks(getNumberOfRunningTasks() + 1);
if (getStatus() == JobStatus.STALLED) {
setStatus(JobStatus.RUNNING);
}
getJobDescriptor().start(td.getId());
td.setStatus(TaskStatus.RUNNING);
td.setStartTime(System.currentTimeMillis());
td.setFinishedTime(-1);
td.setExecutionHostName(td.getExecuterInformation().getHostName() + " (" +
td.getExecuterInformation().getNodeName() + ")");
}
/**
* Start dataspace configuration and application
*/
public void startDataSpaceApplication(NamingService namingService, List<InternalTask> tasks) {
if (taskDataSpaceApplications == null) {
taskDataSpaceApplications = new HashMap<>();
}
for (InternalTask internalTask : tasks) {
long taskId = internalTask.getId().longValue();
// reuse the already configured dataspaceApplication
// if a task restart due to a failure for instance
if (!taskDataSpaceApplications.containsKey(taskId)) {
String appId = internalTask.getId().toString();
TaskDataSpaceApplication taskDataSpaceApplication = new TaskDataSpaceApplication(appId, namingService);
taskDataSpaceApplications.put(taskId, taskDataSpaceApplication);
taskDataSpaceApplication.startDataSpaceApplication(getInputSpace(),
getOutputSpace(),
getGlobalSpace(),
getUserSpace(),
getOwner(),
getId());
}
}
}
/**
* Updates count for running to pending event.
*/
public void newWaitingTask() {
setNumberOfPendingTasks(getNumberOfPendingTasks() + 1);
setNumberOfRunningTasks(getNumberOfRunningTasks() - 1);
if (getNumberOfRunningTasks() == 0 && !(getStatus() == JobStatus.PAUSED || getStatus() == JobStatus.IN_ERROR)) {
setStatus(JobStatus.STALLED);
}
}
/**
* Set this task in restart mode, it will set the task to pending status and
* change task count.
*
* @param task
* the task which has to be restarted.
*/
public void reStartTask(InternalTask task) {
getJobDescriptor().reStart(task.getId());
task.setProgress(0);
if (getStatus() == JobStatus.PAUSED) {
task.setStatus(TaskStatus.PAUSED);
getJobDescriptor().pause(task.getId());
} else {
task.setStatus(TaskStatus.PENDING);
}
}
/**
* Terminate a task, change status, managing dependencies
*
* Also, apply a Control Flow Action if provided. This may alter the number
* of tasks in the job, events have to be sent accordingly.
*
* @param errorOccurred
* has an error occurred for this termination
* @param taskId
* the task to terminate.
* @param frontend
* Used to notify all listeners of the replication of tasks,
* triggered by the FlowAction
* @param action
* a Control Flow Action that will potentially create new tasks
* inside the job
* @return the taskDescriptor that has just been terminated.
*/
public ChangedTasksInfo terminateTask(boolean errorOccurred, TaskId taskId, SchedulerStateUpdate frontend,
FlowAction action, TaskResultImpl result) {
return terminateTask(errorOccurred, taskId, frontend, action, result, false);
}
/**
* Terminate a task, change status, managing dependencies
*
* Also, apply a Control Flow Action if provided. This may alter the number
* of tasks in the job, events have to be sent accordingly.
*
* @param errorOccurred
* has an error occurred for this termination
* @param taskId
* the task to terminate.
* @param frontend
* Used to notify all listeners of the replication of tasks,
* triggered by the FlowAction
* @param action
* a Control Flow Action that will potentially create new tasks
* inside the job
* @return the taskDescriptor that has just been terminated.
*/
public ChangedTasksInfo terminateTask(boolean errorOccurred, TaskId taskId, SchedulerStateUpdate frontend,
FlowAction action, TaskResultImpl result, boolean taskIsPaused) {
final InternalTask descriptor = tasks.get(taskId);
if (!errorOccurred) {
decreaseNumberOfFaultyTasks(taskId);
}
descriptor.setFinishedTime(System.currentTimeMillis());
descriptor.setStatus(errorOccurred ? TaskStatus.FAULTY : TaskStatus.FINISHED);
descriptor.setExecutionDuration(result.getTaskDuration());
if (taskIsPaused) {
setNumberOfInErrorTasks(getNumberOfInErrorTasks() - 1);
} else {
setNumberOfRunningTasks(getNumberOfRunningTasks() - 1);
}
setNumberOfFinishedTasks(getNumberOfFinishedTasks() + 1);
if ((getStatus() == JobStatus.RUNNING) && (getNumberOfRunningTasks() == 0) && !taskIsPaused) {
setStatus(JobStatus.STALLED);
}
ChangedTasksInfo changesInfo = new ChangedTasksInfo();
changesInfo.taskUpdated(descriptor);
boolean didAction = false;
if (action != null) {
InternalTask initiator = tasks.get(taskId);
switch (action.getType()) {
/*
* LOOP action
*/
case LOOP: {
didAction = terminateLoopHandler.terminateLoopTask(action, initiator, changesInfo, frontend);
break;
}
/*
* IF action
*/
case IF: {
didAction = terminateIfTaskHandler.terminateIfTask(action,
initiator,
changesInfo,
frontend,
descriptor,
taskId);
break;
}
/*
* REPLICATE action
*/
case REPLICATE: {
didAction = terminateReplicateTaskHandler.terminateReplicateTask(action,
initiator,
changesInfo,
frontend,
taskId);
break;
}
/*
* CONTINUE action : - continue taskflow as if no action was provided
*/
case CONTINUE:
LOGGER.debug("Task flow Action CONTINUE on task " + initiator.getId().getReadableName());
break;
}
/**
* System.out.println("******** task dump ** " +
* this.getJobInfo().getJobId() + " " + initiator.getName() +
* " does " + action.getType() + " " + ((action.getTarget() == null)
* ? "." : action.getTarget()) + " " + ((action.getTargetElse() ==
* null) ? "." : action.getTargetElse()) + " " +
* ((action.getTargetJoin() == null) ? "." :
* action.getTargetJoin())); for (InternalTask it :
* this.tasks.values()) { System.out.print(it.getName() + " "); if
* (it.getIDependences() != null) { System.out.print("deps "); for
* (InternalTask parent : it.getIDependences()) {
* System.out.print(parent.getName() + " "); } } if
* (it.getIfBranch() != null) { System.out.print("if " +
* it.getIfBranch().getName() + " "); } if (it.getJoinedBranches()
* != null && it.getJoinedBranches().size() == 2) {
* System.out.print("join " +
* it.getJoinedBranches().get(0).getName() + " " +
* it.getJoinedBranches().get(1).getName()); } System.out.println();
* } System.out.println("******** task dump ** " +
* this.getJobInfo().getJobId()); System.out.println();
**/
}
// terminate this task
if (!didAction) {
getJobDescriptor().terminate(taskId, taskIsPaused);
}
return changesInfo;
}
/**
* Assign a tag to new duplicated task because of a REPLICATE or LOOP.
*
* @param replicatedTask
* the new duplicated task.
* @param initiator
* the initiator of the duplication.
* @param loopAction
* true if the duplication if after a loop or, false if it is a
* replicate.
* @param action
* the duplication action.
*/
private void assignReplicationTag(InternalTask replicatedTask, InternalTask initiator, boolean loopAction,
FlowAction action) {
StringBuilder buf = new StringBuilder();
if (loopAction) {
buf.append("LOOP-");
buf.append(InternalTask.getInitialName(initiator.getName()));
if (initiator.getReplicationIndex() > 0) {
buf.append("*");
buf.append(initiator.getReplicationIndex());
}
} else {
buf.append("REPLICATE-");
buf.append(initiator.getName());
}
buf.append("-");
if (loopAction) {
String cronExpr = action.getCronExpr();
if (cronExpr.isEmpty()) {
buf.append(replicatedTask.getIterationIndex());
} else {
// cron task: the replication index is the next date that
// matches the cron expression
Date resolvedCron = new Predictor(cronExpr).nextMatchingDate();
SimpleDateFormat dt = new SimpleDateFormat("dd_MM_YY_HH_mm");
buf.append(dt.format(resolvedCron));
}
} else {
buf.append(replicatedTask.getReplicationIndex());
}
replicatedTask.setTag(buf.toString());
}
public boolean replicateForNextLoopIteration(InternalTask initiator, InternalTask target,
ChangedTasksInfo changesInfo, SchedulerStateUpdate frontend, FlowAction action) {
LOGGER.info("LOOP (init:" + initiator.getId() + "; target:" + target.getId() + ")");
// accumulates the tasks between the initiator and the target
Map<TaskId, InternalTask> dup = new HashMap<>();
// replicate the tasks between the initiator and the target
try {
initiator.replicateTree(dup,
target.getId(),
true,
initiator.getReplicationIndex(),
initiator.getIterationIndex());
} catch (ExecutableCreationException e) {
LOGGER.error("", e);
return false;
}
((JobInfoImpl) this.getJobInfo()).setNumberOfPendingTasks(this.getJobInfo().getNumberOfPendingTasks() +
dup.size());
// ensure naming unicity
// time-consuming but safe
for (InternalTask nt : dup.values()) {
boolean ok;
do {
ok = true;
for (InternalTask task : tasks.values()) {
if (nt.getName().equals(task.getName())) {
nt.setIterationIndex(nt.getIterationIndex() + 1);
ok = false;
}
}
} while (!ok);
}
// configure the new tasks
InternalTask newTarget = null;
InternalTask newInit = null;
for (Entry<TaskId, InternalTask> it : dup.entrySet()) {
InternalTask nt = it.getValue();
if (target.getId().equals(it.getKey())) {
newTarget = nt;
}
if (initiator.getId().equals(it.getKey())) {
newInit = nt;
}
nt.setJobInfo(getJobInfo());
this.addTask(nt);
assignReplicationTag(nt, initiator, true, action);
}
changesInfo.newTasksAdded(dup.values());
// connect replicated tree
newTarget.addDependence(initiator);
changesInfo.taskUpdated(newTarget);
// connect mergers
List<InternalTask> mergers = new ArrayList<>();
for (InternalTask t : this.tasks.values()) {
if (t.getIDependences() != null) {
for (InternalTask p : t.getIDependences()) {
if (p.getId().equals(initiator.getId())) {
if (!t.equals(newTarget)) {
mergers.add(t);
}
}
}
}
}
for (InternalTask t : mergers) {
t.getIDependences().remove(initiator);
t.addDependence(newInit);
changesInfo.taskUpdated(t);
}
// propagate the changes in the job descriptor
getJobDescriptor().doLoop(initiator.getId(), dup, newTarget, newInit);
this.jobInfo.setTasksChanges(changesInfo, this);
// notify frontend that tasks were added and modified
frontend.jobStateUpdated(this.getOwner(),
new NotificationData<JobInfo>(SchedulerEvent.TASK_REPLICATED,
new JobInfoImpl(jobInfo)));
frontend.jobUpdatedFullData(this);
this.jobInfo.clearTasksChanges();
return true;
}
/**
* Walk up <code>down</code>'s dependences until a task <code>name</code> is
* met
*
* also walks weak references created by {@link FlowActionType#IF}
*
* @return the task names <code>name</code>, or null
*/
public InternalTask findTaskUp(String name, InternalTask down) {
InternalTask ret = null;
List<InternalTask> ideps = new ArrayList<>();
if (down.getIDependences() != null) {
ideps.addAll(down.getIDependences());
}
if (down.getJoinedBranches() != null) {
ideps.addAll(down.getJoinedBranches());
}
if (down.getIfBranch() != null) {
ideps.add(down.getIfBranch());
}
for (InternalTask up : ideps) {
if (up.getName().equals(name)) {
ret = up;
} else {
InternalTask r = findTaskUp(name, up);
if (r != null) {
ret = r;
}
}
}
return ret;
}
/**
* Simulate that a task have been started and terminated. Used only by the
* recovery method in scheduler core.
*
* @param id
* the id of the task to start and terminate.
*/
public void recoverTask(TaskId id) {
getJobDescriptor().recoverTask(id);
}
/**
* Failed this job due to the given task failure or job has been killed
*
* @param taskId
* the task that has been the cause to failure. Can be null if
* the job has been killed
* @param jobStatus
* type of the failure on this job. (failed/canceled/killed)
*/
public Set<TaskId> failed(TaskId taskId, JobStatus jobStatus) {
if (jobStatus != JobStatus.KILLED) {
InternalTask descriptor = tasks.get(taskId);
if (descriptor.getStartTime() > 0) {
descriptor.setFinishedTime(System.currentTimeMillis());
setNumberOfFinishedTasks(getNumberOfFinishedTasks() + 1);
if (descriptor.getExecutionDuration() < 0) {
descriptor.setExecutionDuration(descriptor.getFinishedTime() - descriptor.getStartTime());
}
}
descriptor.setStatus((jobStatus == JobStatus.FAILED) ? TaskStatus.FAILED : TaskStatus.FAULTY);
// terminate this job descriptor
getJobDescriptor().failed();
}
// set the new status of the job
setFinishedTime(System.currentTimeMillis());
setNumberOfPendingTasks(0);
setNumberOfRunningTasks(0);
setStatus(jobStatus);
// creating list of status
Set<TaskId> updatedTasks = new HashSet<>();
for (InternalTask td : tasks.values()) {
if (!td.getId().equals(taskId)) {
if (td.getStatus() == TaskStatus.RUNNING) {
td.setStatus(TaskStatus.ABORTED);
td.setFinishedTime(System.currentTimeMillis());
if (td.getStartTime() > 0 && td.getExecutionDuration() < 0) {
td.setExecutionDuration(td.getFinishedTime() - td.getStartTime());
}
updatedTasks.add(td.getId());
} else if (td.getStatus() == TaskStatus.WAITING_ON_ERROR ||
td.getStatus() == TaskStatus.WAITING_ON_FAILURE) {
td.setStatus(TaskStatus.NOT_RESTARTED);
updatedTasks.add(td.getId());
} else if (td.getStatus() != TaskStatus.FINISHED && td.getStatus() != TaskStatus.FAILED &&
td.getStatus() != TaskStatus.FAULTY && td.getStatus() != TaskStatus.SKIPPED) {
td.setStatus(TaskStatus.NOT_STARTED);
updatedTasks.add(td.getId());
}
}
}
terminateTaskDataSpaceApplications();
return updatedTasks;
}
/**
* Get a task descriptor that is in the running task queue.
*
* @param id
* the id of the task descriptor to retrieve.
* @return the task descriptor associated to this id, or null if not
* running.
*/
public TaskDescriptor getRunningTaskDescriptor(TaskId id) {
return getJobDescriptor().GetRunningTaskDescriptor(id);
}
/**
* Set all properties following a job submitting.
*/
public void submitAction() {
setSubmittedTime(System.currentTimeMillis());
setStatus(JobStatus.PENDING);
}
/**
* Prepare tasks in order to be ready to be scheduled. The task may have a
* consistent id and job info.
*/
public synchronized void prepareTasks() {
// get tasks
ArrayList<InternalTask> sorted = getITasks();
// sort task according to the ID
Collections.sort(sorted);
tasks.clear();
// re-init taskId
int id = 0;
for (InternalTask td : sorted) {
TaskId newId = TaskIdImpl.createTaskId(getId(), td.getName(), id++);
td.setId(newId);
td.setJobInfo(getJobInfo());
tasks.put(newId, td);
}
}
/**
* Set all properties in order to start the job. After this method and for
* better performances you may have to set the taskStatusModify to "null" :
* setTaskStatusModify(null);
*/
public void start() {
setStartTime(System.currentTimeMillis());
setNumberOfPendingTasks(getTotalNumberOfTasks());
setNumberOfRunningTasks(0);
setStatus(JobStatus.RUNNING);
List<InternalTask> internalTasks = getITasks();
HashMap<TaskId, TaskStatus> taskStatus = new HashMap<>(internalTasks.size());
for (InternalTask internalTask : internalTasks) {
internalTask.setStatus(TaskStatus.PENDING);
taskStatus.put(internalTask.getId(), TaskStatus.PENDING);
}
}
/**
* Set all properties in order to terminate the job.
*/
public void terminate() {
long finishTime = System.currentTimeMillis();
setStatus(JobStatus.FINISHED);
setFinishedTime(finishTime);
terminateTaskDataSpaceApplications();
}
private void terminateTaskDataSpaceApplications() {
if (taskDataSpaceApplications != null) {
for (TaskDataSpaceApplication taskDataSpaceApplication : taskDataSpaceApplications.values()) {
taskDataSpaceApplication.terminateDataSpaceApplication();
}
}
}
/**
* Paused every running and submitted tasks in this pending job. After this
* method and for better performances you may have to set the
* taskStatusModify to "null" : setTaskStatusModify(null);
*
* @return true if the job has correctly been paused, false if not.
*/
public Set<TaskId> setPaused() {
if (jobInfo.getStatus() == JobStatus.PAUSED) {
return new HashSet<>(0);
}
jobInfo.setStatus(JobStatus.PAUSED);
Collection<InternalTask> values = tasks.values();
Set<TaskId> updatedTasks = new HashSet<>(values.size());
for (InternalTask td : values) {
if ((td.getStatus() != TaskStatus.FINISHED) && (td.getStatus() != TaskStatus.RUNNING) &&
(td.getStatus() != TaskStatus.SKIPPED) && (td.getStatus() != TaskStatus.FAULTY) &&
(td.getStatus() != TaskStatus.IN_ERROR)) {
td.setStatus(TaskStatus.PAUSED);
getJobDescriptor().pause(td.getId());
updatedTasks.add(td.getId());
}
}
return updatedTasks;
}
public void setTaskPausedOnError(InternalTask internalTask) {
internalTask.setStatus(TaskStatus.IN_ERROR);
getJobDescriptor().pausedTaskOnError(internalTask.getId());
}
/**
* Status of every paused tasks becomes pending or submitted in this pending
* job. After this method and for better performances you may have to set
* the taskStatusModify to "null" : setTaskStatusModify(null);
*
* @return true if the job has correctly been unpaused, false if not.
*/
public Set<TaskId> setUnPause() {
if (jobInfo.getStatus() != JobStatus.PAUSED && jobInfo.getStatus() != JobStatus.IN_ERROR) {
return new HashSet<>(0);
}
boolean jobContainsInErrorTasks = false;
if ((getNumberOfPendingTasks() + getNumberOfRunningTasks() + getNumberOfFinishedTasks()) == 0) {
jobInfo.setStatus(JobStatus.PENDING);
} else if (getNumberOfRunningTasks() == 0) {
jobInfo.setStatus(JobStatus.STALLED);
} else {
jobInfo.setStatus(JobStatus.RUNNING);
}
Set<TaskId> updatedTasks = new HashSet<>();
for (InternalTask task : tasks.values()) {
if (jobInfo.getStatus() == JobStatus.PENDING) {
task.setStatus(TaskStatus.SUBMITTED);
updatedTasks.add(task.getId());
} else if ((jobInfo.getStatus() == JobStatus.RUNNING) || (jobInfo.getStatus() == JobStatus.STALLED)) {
if ((task.getStatus() != TaskStatus.FINISHED) && (task.getStatus() != TaskStatus.RUNNING) &&
(task.getStatus() != TaskStatus.SKIPPED) && (task.getStatus() != TaskStatus.FAULTY) &&
(task.getStatus() != TaskStatus.IN_ERROR)) {
task.setStatus(TaskStatus.PENDING);
updatedTasks.add(task.getId());
}
}
if (task.getStatus() == TaskStatus.IN_ERROR) {
jobContainsInErrorTasks = true;
} else {
getJobDescriptor().unpause(task.getId());
}
}
if (jobContainsInErrorTasks) {
jobInfo.setStatus(JobStatus.IN_ERROR);
}
return updatedTasks;
}
public ChangedTasksInfo finishInErrorTask(TaskId taskId, TaskResultImpl result, SchedulerStateUpdate frontend) {
FlowAction action = result.getAction();
ChangedTasksInfo changedTasksInfo = terminateTask(false, taskId, frontend, action, result, true);
setUnPause();
return changedTasksInfo;
}
public void restartInErrorTask(InternalTask internalTask) {
if (internalTask.getStatus() == TaskStatus.IN_ERROR) {
newWaitingTask();
setNumberOfInErrorTasks(getNumberOfInErrorTasks() - 1);
internalTask.setStatus(TaskStatus.PENDING);
getJobDescriptor().unpause(internalTask.getId());
}
}
/**
* @see org.ow2.proactive.scheduler.common.job.Job#setPriority(org.ow2.proactive.scheduler.common.job.JobPriority)
*/
@Override
public void setPriority(JobPriority priority) {
jobInfo.setPriority(priority);
}
/**
* @see org.ow2.proactive.scheduler.common.job.JobState#getTasks()
*/
@Override
public ArrayList<TaskState> getTasks() {
return new ArrayList<TaskState>(tasks.values());
}
public void setTasks(Collection<InternalTask> tasksList) {
tasks = new HashMap<>(tasksList.size());
for (InternalTask task : tasksList) {
tasks.put(task.getId(), task);
}
}
/**
* @see org.ow2.proactive.scheduler.common.job.JobState#getHMTasks()
*/
@Override
public Map<TaskId, TaskState> getHMTasks() {
Set<Entry<TaskId, InternalTask>> entries = tasks.entrySet();
Map<TaskId, TaskState> tmp = new HashMap<>(entries.size());
for (Entry<TaskId, InternalTask> e : entries) {
tmp.put(e.getKey(), e.getValue());
}
return tmp;
}
/**
* To get the tasks as an array list.
*
* @return the tasks
*/
public ArrayList<InternalTask> getITasks() {
return new ArrayList<>(tasks.values());
}
/**
* To get the tasks as a hash map.
*
* @return the tasks
*/
public Map<TaskId, InternalTask> getIHMTasks() {
return tasks;
}
/**
* To set the id
*
* @param id
* the id to set
*/
public void setId(JobId id) {
jobInfo.setJobId(id);
}
/**
* To set the finishedTime
*
* @param finishedTime
* the finishedTime to set
*/
public void setFinishedTime(long finishedTime) {
jobInfo.setFinishedTime(finishedTime);
}
/**
* To set the startTime
*
* @param startTime
* the startTime to set
*/
public void setStartTime(long startTime) {
jobInfo.setStartTime(startTime);
}
/**
* To set the inErrorTime
*
* @param inErrorTime
* the inErrorTime to set
*/
public void setInErrorTime(long inErrorTime) {
jobInfo.setInErrorTime(inErrorTime);
}
/**
* To set the submittedTime
*
* @param submittedTime
* the submittedTime to set
*/
public void setSubmittedTime(long submittedTime) {
jobInfo.setSubmittedTime(submittedTime);
}
/**
* To set the removedTime
*
* @param removedTime
* the removedTime to set
*/
public void setRemovedTime(long removedTime) {
jobInfo.setRemovedTime(removedTime);
}
/**
* To set the numberOfFinishedTasks
*
* @param numberOfFinishedTasks
* the numberOfFinishedTasks to set
*/
public void setNumberOfFinishedTasks(int numberOfFinishedTasks) {
jobInfo.setNumberOfFinishedTasks(numberOfFinishedTasks);
}
/**
* To set the numberOfPendingTasks
*
* @param numberOfPendingTasks
* the numberOfPendingTasks to set
*/
public void setNumberOfPendingTasks(int numberOfPendingTasks) {
jobInfo.setNumberOfPendingTasks(numberOfPendingTasks);
}
/**
* To set the numberOfRunningTasks
*
* @param numberOfRunningTasks
* the numberOfRunningTasks to set
*/
public void setNumberOfRunningTasks(int numberOfRunningTasks) {
jobInfo.setNumberOfRunningTasks(numberOfRunningTasks);
}
public void incrementNumberOfFailedTasksBy(int increment) {
jobInfo.setNumberOfFailedTasks(getNumberOfFailedTasks() + increment);
}
/**
* To set the numberOfFailedTasks
*
* @param numberOfFailedTasks
* the numberOfFailedTasks to set
*/
public void setNumberOfFailedTasks(int numberOfFailedTasks) {
jobInfo.setNumberOfFailedTasks(numberOfFailedTasks);
}
public boolean saveFaultyTaskId(TaskId taskId) {
return faultyTasks.add(taskId);
}
public void increaseNumberOfFaultyTasks(TaskId taskId) {
if (faultyTasks.add(taskId)) {
incrementNumberOfFaultyTasksBy(1);
}
}
public void decreaseNumberOfFaultyTasks(TaskId taskId) {
if (faultyTasks.remove(taskId)) {
incrementNumberOfFaultyTasksBy(-1);
}
}
public void incrementNumberOfFaultyTasksBy(int increment) {
jobInfo.setNumberOfFaultyTasks(getNumberOfFaultyTasks() + increment);
}
/**
* To set the numberOfFaultyTasks
*
* @param numberOfFaultyTasks
* the numberOfFaultyTasks to set
*/
public void setNumberOfFaultyTasks(int numberOfFaultyTasks) {
jobInfo.setNumberOfFaultyTasks(numberOfFaultyTasks);
}
public void incrementNumberOfInErrorTasksBy(int increment) {
jobInfo.setNumberOfInErrorTasks(getNumberOfInErrorTasks() + increment);
}
/**
* To set the numberOfInErrorTasks
*
* @param numberOfInErrorTasks
* the numberOfInErrorTasks to set
*/
public void setNumberOfInErrorTasks(int numberOfInErrorTasks) {
jobInfo.setNumberOfInErrorTasks(numberOfInErrorTasks);
}
/**
* To get the jobDescriptor
*
* @return the jobDescriptor
*/
@XmlTransient
public JobDescriptorImpl getJobDescriptor() {
if (jobDescriptor == null) {
jobDescriptor = new JobDescriptorImpl(this);
}
return (JobDescriptorImpl) jobDescriptor;
}
/**
* Set the job Descriptor
*
* @param jobD
* the JobDescriptor to set.
*/
public void setJobDescriptor(JobDescriptor jobD) {
this.jobDescriptor = jobD;
}
/**
* @param status
* the status to set
*/
public void setStatus(JobStatus status) {
jobInfo.setStatus(status);
}
/**
* @see org.ow2.proactive.scheduler.common.job.JobState#getOwner()
*/
@Override
public String getOwner() {
return jobInfo.getJobOwner();
}
/**
* To set the owner of this job.
*
* @param owner
* the owner to set.
*/
public void setOwner(String owner) {
this.jobInfo.setJobOwner(owner);
}
/**
* Get the credentials for this job
*
* @return the credentials for this job
*/
public Credentials getCredentials() {
return credentials;
}
/**
* Set the credentials value to the given credentials value
*
* @param credentials
* the credentials to set
*/
public void setCredentials(Credentials credentials) {
this.credentials = credentials;
}
/**
* Get the next restart waiting time in millis.
*
* @return the next restart waiting time in millis.
*/
public long getNextWaitingTime(int executionNumber) {
if (executionNumber <= 0) {
// execution number is 0 or less, restart with the minimal amount of
// time
return restartWaitingTimer;
} else if (executionNumber > 10) {
// execution timer exceed 10, restart after 60 seconds
return 60000;
} else {
// else restart according to this function
return (getNextWaitingTime(executionNumber - 1) + executionNumber * 1000);
}
}
/**
* Set this job to the state toBeRemoved.
*/
public void setToBeRemoved() {
jobInfo.setToBeRemoved();
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (!(obj instanceof InternalJob))
return false;
InternalJob other = (InternalJob) obj;
if (!getId().equals(other.getId()))
return false;
return true;
}
/**
* Get the jobDataSpaceApplication
*
* @return the jobDataSpaceApplication
*/
public Map<Long, TaskDataSpaceApplication> getTaskDataSpaceApplications() {
return taskDataSpaceApplications;
}
/**
* Return the internal task associated with the given task name for this
* job.
*
* @param taskName
* the task name to find
* @return the internal task associated with the given name.
* @throws UnknownTaskException
* if the given taskName does not exist.
*/
public InternalTask getTask(String taskName) throws UnknownTaskException {
for (InternalTask task : tasks.values()) {
if (task.getId().getReadableName().equals(taskName)) {
return task;
}
}
throw new UnknownTaskException("'" + taskName + "' does not exist in this job.");
}
public InternalTask getTask(TaskId taskId) throws UnknownTaskException {
InternalTask task = tasks.get(taskId);
if (task != null) {
return task;
} else {
throw new UnknownTaskException("'" + taskId + "' does not exist in this job.");
}
}
@Override
public String display() {
String nl = System.lineSeparator();
String answer = super.display();
return answer + nl + "\tTasks = " + displayAllTasks();
}
private String displayAllTasks() {
String nl = System.lineSeparator();
String answer = "{" + nl;
for (TaskId tid : tasks.keySet()) {
answer += tasks.get(tid).display() + nl + nl;
}
answer += "}";
return answer;
}
public Job getTaskFlowJob() {
return taskFlowJob;
}
public void setTaskFlowJob(Job taskFlowJob) {
this.taskFlowJob = taskFlowJob;
}
public long getScheduledTimeForRemoval() {
return this.jobInfo.getScheduledTimeForRemoval();
}
public void setScheduledTimeForRemoval(long scheduledTimeForRemoval) {
this.jobInfo.setScheduledTimeForRemoval(scheduledTimeForRemoval);
}
}