package org.ovirt.engine.core.bll; import java.util.concurrent.atomic.AtomicInteger; import org.ovirt.engine.core.common.action.VdcReturnValueBase; import org.ovirt.engine.core.common.asynctasks.AsyncTaskParameters; import org.ovirt.engine.core.common.asynctasks.EndedTaskInfo; import org.ovirt.engine.core.common.asynctasks.SetTaskGroupStatusVisitor; import org.ovirt.engine.core.compat.LogCompat; import org.ovirt.engine.core.compat.LogFactoryCompat; import org.ovirt.engine.core.utils.threadpool.ThreadPoolUtil; /** * EntityAsyncTask: Base class for all tasks regarding a specific entity (VM, * VmTemplate). The 'OnAfterEntityTaskEnded' method will be executed only if all * other tasks regarding the relevant entity have already ended. */ public class EntityAsyncTask extends SPMAsyncTask { protected static final Object _lockObject = new Object(); private static java.util.HashMap<Object, EntityMultiAsyncTasks> _multiTasksByEntities = new java.util.HashMap<Object, EntityMultiAsyncTasks>(); private static AtomicInteger _endActionsInProgress = new AtomicInteger(0); public static int getEndActionsInProgress() { return _endActionsInProgress.get(); } private static EntityMultiAsyncTasks GetEntityMultiAsyncTasksByContainerId(Object containerID) { EntityMultiAsyncTasks entityInfo = null; synchronized (_lockObject) { if (_multiTasksByEntities.containsKey(containerID) && _multiTasksByEntities.get(containerID) != null) { entityInfo = _multiTasksByEntities.get(containerID); } } return entityInfo; } private EntityMultiAsyncTasks GetEntityMultiAsyncTasks() { return GetEntityMultiAsyncTasksByContainerId(getContainerId()); } public EntityAsyncTask(AsyncTaskParameters parameters) { super(parameters); synchronized (_lockObject) { if (!_multiTasksByEntities.containsKey(getContainerId())) { log.infoFormat("EntityAsyncTask::Adding EntityMultiAsyncTasks object for entity '{0}'", getContainerId()); _multiTasksByEntities.put(getContainerId(), new EntityMultiAsyncTasks(getContainerId())); } } EntityMultiAsyncTasks entityInfo = GetEntityMultiAsyncTasks(); entityInfo.AttachTask(this); } @Override protected void ConcreteStartPollingTask() { EntityMultiAsyncTasks entityInfo = GetEntityMultiAsyncTasks(); entityInfo.StartPollingTask(getTaskID()); } @Override protected void OnTaskEndSuccess() { LogEndTaskSuccess(); OnCurrentTaskEndSuccess(); EndActionIfNecessary(); } private void EndActionIfNecessary() { EntityMultiAsyncTasks entityInfo = GetEntityMultiAsyncTasks(); if (entityInfo == null) { log.warnFormat( "EntityAsyncTask::EndActionIfNecessary: No info is available for entity '{0}', current task ('{1}') was probably created while other tasks were in progress, clearing task.", getContainerId(), getTaskID()); ClearAsyncTask(); } else if (entityInfo.ShouldEndAction()) { log.infoFormat( "EntityAsyncTask::EndActionIfNecessary: All tasks of entity '{0}' has ended -> executing 'EndAction'", getContainerId()); log.infoFormat( "EntityAsyncTask::EndAction: Ending action for {0} tasks (entity ID: '{1}'): calling EndAction for action type '{2}'.", entityInfo.getTasksCountCurrentActionType(), entityInfo.getContainerId(), entityInfo.getActionType()); entityInfo.MarkAllWithAttemptingEndAction(); _endActionsInProgress.incrementAndGet(); ThreadPoolUtil.execute(new Runnable() { @Override public void run() { EndCommandAction(null); } }); } } private void EndCommandAction(Object data) { EntityMultiAsyncTasks entityInfo = GetEntityMultiAsyncTasks(); VdcReturnValueBase vdcReturnValue = null; boolean success = true; for (EndedTaskInfo taskInfo : entityInfo.getEndedTasksInfo().getTasksInfo()) { success = taskInfo.getTaskStatus().getTaskEndedSuccessfully(); if (!success) { break; } } // set all task to success/fail for (EndedTaskInfo taskInfo : entityInfo.getEndedTasksInfo().getTasksInfo()) { this.getParameters().getDbAsyncTask().getaction_parameters() .Accept(taskInfo, new SetTaskGroupStatusVisitor(success)); } try { log.infoFormat("EntityAsyncTask::EndCommandAction [within thread]: Attempting to EndAction '{0}'", entityInfo.getActionType()); try { vdcReturnValue = Backend.getInstance().EndAction(entityInfo.getActionType(), this.getParameters().getDbAsyncTask().getaction_parameters()); } catch (RuntimeException Ex) { String errorMessage = String .format("EntityAsyncTask::EndCommandAction [within thread]: EndAction for action type %1$s threw an exception", entityInfo.getActionType()); log.error(errorMessage, Ex); } } catch (RuntimeException Ex2) { log.error( "EntityAsyncTask::EndCommandAction [within thread]: An exception has been thrown (not related to 'EndAction' itself)", Ex2); } finally { HandleEndActionResult(entityInfo, vdcReturnValue); _endActionsInProgress.decrementAndGet(); } } private static void HandleEndActionResult(EntityMultiAsyncTasks entityInfo, VdcReturnValueBase vdcReturnValue) { try { if (entityInfo != null) { log.infoFormat( "EntityAsyncTask::HandleEndActionResult [within thread]: EndAction for action type '{0}' completed, handling the result.", entityInfo.getActionType()); if (vdcReturnValue == null || (!vdcReturnValue.getSucceeded() && vdcReturnValue.getEndActionTryAgain())) { log.infoFormat( "EntityAsyncTask::HandleEndActionResult [within thread]: EndAction for action type {0} hasn't succeeded, not clearing tasks, will attempt again next polling.", entityInfo.getActionType()); entityInfo.Repoll(); } else { log.infoFormat( "EntityAsyncTask::HandleEndActionResult [within thread]: EndAction for action type {0} {1}succeeded, clearing tasks.", entityInfo.getActionType(), (vdcReturnValue.getSucceeded() ? "" : "hasn't ")); entityInfo.ClearTasks(); synchronized (_lockObject) { if (entityInfo.getAllCleared()) { log.infoFormat( "EntityAsyncTask::HandleEndActionResult [within thread]: Removing EntityMultiAsyncTasks object for entity '{0}'", entityInfo.getContainerId()); _multiTasksByEntities.remove(entityInfo.getContainerId()); } else { entityInfo.resetActionTypeIfNecessary(); entityInfo.StartPollingNextTask(); } } } } } catch (RuntimeException ex) { log.error("EntityAsyncTask::HandleEndActionResult [within thread]: an exception has been thrown", ex); } } @Override protected void OnTaskEndFailure() { LogEndTaskFailure(); OnCurrentTaskEndFailure(); EndActionIfNecessary(); } @Override protected void OnTaskDoesNotExist() { LogTaskDoesntExist(); OnCurrentTaskDoesNotExist(); EndActionIfNecessary(); } /** * Executed when the task ends successfully, regardless of whether there are * other running tasks on the same entity or not. */ protected void OnCurrentTaskEndSuccess() { } /** * Executed when the task ended with a failure, regardless of whether there * are other running tasks on the same entity or not. */ protected void OnCurrentTaskEndFailure() { } /** * Executed when we find out that the task doesn't exist, regardless of * whether there are other running tasks on the same entity or not. */ protected void OnCurrentTaskDoesNotExist() { } private static LogCompat log = LogFactoryCompat.getLog(EntityAsyncTask.class); }