/* * 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.termination.handlers; import java.util.ArrayList; import java.util.List; import org.apache.log4j.Logger; import org.ow2.proactive.scheduler.common.NotificationData; import org.ow2.proactive.scheduler.common.SchedulerEvent; import org.ow2.proactive.scheduler.common.task.TaskId; import org.ow2.proactive.scheduler.common.task.TaskStatus; import org.ow2.proactive.scheduler.common.task.flow.FlowAction; import org.ow2.proactive.scheduler.core.SchedulerStateUpdate; import org.ow2.proactive.scheduler.job.ChangedTasksInfo; import org.ow2.proactive.scheduler.job.InternalJob; import org.ow2.proactive.scheduler.job.JobInfoImpl; import org.ow2.proactive.scheduler.task.internal.InternalTask; public class TerminateIfTaskHandler { private static final Logger LOGGER = Logger.getLogger(TerminateIfTaskHandler.class); private final InternalJob internalJob; public TerminateIfTaskHandler(InternalJob internalJob) { this.internalJob = internalJob; } public boolean terminateIfTask(FlowAction action, InternalTask initiator, ChangedTasksInfo changesInfo, SchedulerStateUpdate frontend, InternalTask descriptor, TaskId taskId) { InternalTask[] targets = searchIfElseJoinTasks(action, initiator); // the targetIf from action getTarget is the selected branch // the IF condition has already been evaluated prior to being put in a // FlowAction // the targetElse from action getTargetElse is the branch that was NOT // selected InternalTask targetIf = targets[0]; InternalTask targetElse = targets[1]; InternalTask targetJoin = targets[2]; LOGGER.info("Control Flow Action IF: " + targetIf.getId() + " join: " + ((targetJoin == null) ? "null" : targetJoin.getId())); // these 2 tasks delimit the Task Block formed by the IF branch InternalTask branchStart = targetIf; InternalTask branchEnd = null; String match = targetIf.getMatchingBlock(); if (match != null) { for (InternalTask t : internalJob.getIHMTasks().values()) { if (match.equals(t.getName()) && !(t.getStatus().equals(TaskStatus.FINISHED) || t.getStatus().equals(TaskStatus.SKIPPED))) { branchEnd = t; } } } // no matching block: there is no block, the branch is a single task if (branchEnd == null) { branchEnd = targetIf; } // plug the branch branchStart.addDependence(initiator); changesInfo.taskUpdated(branchStart); if (targetJoin != null) { targetJoin.addDependence(branchEnd); changesInfo.taskUpdated(targetJoin); } // the other branch will not be executed // first, find the concerned tasks List<InternalTask> elseTasks = new ArrayList<>(); // elseTasks.add(targetElse); for (InternalTask t : internalJob.getIHMTasks().values()) { if (t.dependsOn(targetElse)) { elseTasks.add(t); } } // even though the targetElse is not going to be executed, a // dependency on initiator still makes sense and would help // reconstruct the job graph on the client targetElse.addDependence(initiator); changesInfo.taskUpdated(targetElse); for (InternalTask it : elseTasks) { it.setFinishedTime(descriptor.getFinishedTime() + 1); it.setStatus(TaskStatus.SKIPPED); it.setExecutionDuration(0); changesInfo.taskSkipped(it); internalJob.setNumberOfPendingTasks(internalJob.getNumberOfPendingTasks() - 1); internalJob.setNumberOfFinishedTasks(internalJob.getNumberOfFinishedTasks() + 1); LOGGER.info("Task " + it.getId() + " will not be executed"); } // plug the branch in the descriptor TaskId joinId = null; if (targetJoin != null) { joinId = targetJoin.getId(); } internalJob.getJobDescriptor().doIf(initiator.getId(), branchStart.getId(), branchEnd.getId(), joinId, targetElse.getId(), elseTasks); ((JobInfoImpl) internalJob.getJobInfo()).setTasksChanges(changesInfo, internalJob); // notify frontend that tasks were modified if (frontend != null) { frontend.jobStateUpdated(internalJob.getOwner(), new NotificationData<>(SchedulerEvent.TASK_SKIPPED, internalJob.getJobInfo())); frontend.jobUpdatedFullData(internalJob); } ((JobInfoImpl) internalJob.getJobInfo()).clearTasksChanges(); // no jump is performed ; now that the tasks have been plugged // the flow can continue its normal operation internalJob.getJobDescriptor().terminate(taskId); return true; } private InternalTask[] searchIfElseJoinTasks(FlowAction action, InternalTask initiator) { InternalTask targetIf = null; InternalTask targetElse = null; InternalTask targetJoin = null; // search for the targets as perfect matches of the unique name for (InternalTask it : internalJob.getIHMTasks().values()) { // target is finished : probably looped if (it.getStatus().equals(TaskStatus.FINISHED) || it.getStatus().equals(TaskStatus.SKIPPED)) { continue; } if (action.getTarget().equals(it.getName())) { if (it.getIfBranch().equals(initiator)) { targetIf = it; } } else if (action.getTargetElse().equals(it.getName())) { if (it.getIfBranch().equals(initiator)) { targetElse = it; } } else if (action.getTargetContinuation().equals(it.getName())) { InternalTask up = internalJob.findTaskUp(initiator.getName(), it); if (up != null && up.equals(initiator)) { targetJoin = it; } } } boolean searchIf = (targetIf == null); boolean searchElse = (targetElse == null); boolean searchJoin = (targetJoin == null); // search of a runnable perfect match for the targets failed; // the natural target was iterated, need to find the next iteration // which is the the one with the same dup index and base name, // but the highest iteration index for (InternalTask it : internalJob.getIHMTasks().values()) { // does not share the same dup index : cannot be the same scope if (it.getReplicationIndex() != initiator.getReplicationIndex()) { continue; } if (it.getStatus().equals(TaskStatus.FINISHED) || it.getStatus().equals(TaskStatus.SKIPPED)) { continue; } String name = InternalTask.getInitialName(it.getName()); if (searchIf && InternalTask.getInitialName(action.getTarget()).equals(name)) { if (targetIf == null || targetIf.getIterationIndex() < it.getIterationIndex()) { targetIf = it; } } else if (searchElse && InternalTask.getInitialName(action.getTargetElse()).equals(name)) { if (targetElse == null || targetElse.getIterationIndex() < it.getIterationIndex()) { targetElse = it; } } else if (searchJoin && InternalTask.getInitialName(action.getTargetContinuation()).equals(name)) { if (targetJoin == null || targetJoin.getIterationIndex() < it.getIterationIndex()) { targetJoin = it; } } } InternalTask[] result = { targetIf, targetElse, targetJoin }; return result; } }