<commit_before>package org.ovirt.engine.core.bll.job;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.ovirt.engine.core.bll.CommandBase;
import org.ovirt.engine.core.bll.context.CommandContext;
import org.ovirt.engine.core.bll.job.ExecutionContext.ExecutionMethod;
import org.ovirt.engine.core.common.VdcObjectType;
import org.ovirt.engine.core.common.action.VdcActionParametersBase;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.action.VdcReturnValueBase;
import org.ovirt.engine.core.common.job.ExternalSystemType;
import org.ovirt.engine.core.common.job.Job;
import org.ovirt.engine.core.common.job.JobExecutionStatus;
import org.ovirt.engine.core.common.job.Step;
import org.ovirt.engine.core.common.job.StepEnum;
import org.ovirt.engine.core.common.utils.ValidationUtils;
import org.ovirt.engine.core.common.validation.group.PreRun;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.compat.NGuid;
import org.ovirt.engine.core.dal.dbbroker.DbFacade;
import org.ovirt.engine.core.dal.job.ExecutionMessageDirector;
import org.ovirt.engine.core.utils.log.Log;
import org.ovirt.engine.core.utils.log.LogFactory;
import org.ovirt.engine.core.utils.log.LoggedUtils;

/**
 * Provides methods for managing the flow objects the of the command, by the given execution context o the command.
 * <ul>
 * <li>Creates an {@code ExecutionContext} instance for {@code CommandBase}.
 * <li>Creates {@Job} entities.
 * <li>Add {@code Step} to a {@code Job}
 * <li>Add {@code Step} to a {@code Step} as a sub step.
 * <li>End step.
 * <li>End job.
 * <ul>
 */
public class ExecutionHandler {

    private static Log log = LogFactory.getLog(ExecutionHandler.class);

    private static final List<Class<?>> validationGroups = Arrays.asList(new Class<?>[] { PreRun.class });

    /**
     * Creates and returns an instance of {@link Job} entity.
     *
     * @param actionType
     *            The action type the job entity represents.
     * @param command
     *            The {@code CommandBase} instance which the job entity describes.
     * @return An initialized {@code Job} instance.
     */
    public static Job createJob(VdcActionType actionType, CommandBase<?> command) {
        Job job = new Job();

        job.setId(Guid.NewGuid());
        job.setActionType(actionType);
        job.setDescription(ExecutionMessageDirector.resolveJobMessage(actionType, command.getJobMessageProperties()));
        job.setJobSubjectEntities(getSubjectEntities(command.getPermissionCheckSubjects()));
        job.setOwnerId(command.getUserId());
        job.setStatus(JobExecutionStatus.STARTED);
        job.setStartTime(new Date());
        job.setCorrelationId(command.getCorrelationId());

        return job;
    }

    private static Map<Guid, VdcObjectType> getSubjectEntities(Map<Guid, VdcObjectType> subjects) {
        Map<Guid, VdcObjectType> entities = new HashMap<Guid, VdcObjectType>();
        VdcObjectType entityType;
        Guid entityId;
        for (Entry<Guid, VdcObjectType> entry : subjects.entrySet()) {
            entityType = entry.getValue();
            entityId = entry.getKey();
            if (entityType != null && entityId != null) {
                entities.put(entityId, entityType);
            }
        }
        return entities;
    }

    /**
     * Finalizes a {@code Step} execution by a given context in which the step was performed and by the exit status of
     * the step.
     *
     * @param context
     *            The context in which the {@code Step} was executed.
     * @param step
     *            The step to finalize.
     * @param exitStatus
     *            Indicates if the execution described by the step ended successfully or not.
     */
    public static void endStep(ExecutionContext context, Step step, boolean exitStatus) {
        if (context == null) {
            return;
        }
        if (context.isMonitored()) {
            context.setCompleted(true);
            Job job = context.getJob();
            try {
                if (context.getExecutionMethod() == ExecutionMethod.AsJob && job != null) {

                    if (exitStatus) {
                        if (step != null) {
                            step.markStepEnded(exitStatus);
                            JobRepositoryFactory.getJobRepository().updateStep(step);
                        }
                    } else {
                        // step failure will cause the job to be marked as failed
                        job.markJobEnded(false);
                        JobRepositoryFactory.getJobRepository().updateCompletedJobAndSteps(job);
                    }
                } else {
                    Step parentStep = context.getStep();
                    if (context.getExecutionMethod() == ExecutionMethod.AsStep && parentStep != null) {
                        if (exitStatus) {
                            if (step != null) {
                                step.markStepEnded(exitStatus);
                                JobRepositoryFactory.getJobRepository().updateStep(step);
                            }
                        }
                    }
                }
            } catch (Exception e) {
                log.error(e);
            }
        }
    }

    /**
     * Finalizes a {@code Step} execution which represents a VDSM task. In case of a failure status, the job will not be
     * marked as failed at this stage, but via executing the {@code CommandBase.endAction} with the proper status by
     * {@code the AsyncTaskManager}.
     *
     * @param stepId
     *            A unique identifier of the step to finalize.
     * @param exitStatus
     *            The status which the step should be ended with.
     */
    public static void endTaskStep(NGuid stepId, JobExecutionStatus exitStatus) {
        try {
            if (stepId != null) {
                Step step = JobRepositoryFactory.getJobRepository().getStep(stepId.getValue());

                if (step != null) {
                    step.markStepEnded(exitStatus);
                    JobRepositoryFactory.getJobRepository().updateStep(step);
                }
            }
        } catch (Exception e) {
            log.errorFormat("Failed to terminate step {0} with status {1}", stepId, exitStatus, e);
        }
    }

    /**
     * Prepares the monitoring objects for the command by the default behavior:
     * <ul>
     * <li> {@link ExecutionContext} determines how the command should be monitored. By default, non-internal commands
     * will be associated with {@code Job} to represent the command execution. Internal commands will not be monitored
     * by default, therefore the {@code ExecutionContext} is created as non-monitored context.
     * <li> {@link Job} is created for monitored actions
     * </ul>
     *
     * @param command
     *            The created instance of the command (can't be <code>null</code>).
     * @param actionType
     *            The action type of the command
     * @param runAsInternal
     *            Indicates if the command should be run as internal action or not
     */
    public static void prepareCommandForMonitoring(CommandBase<?> command,
            VdcActionType actionType,
            boolean runAsInternal) {

        ExecutionContext context = command.getExecutionContext();
        if (context == null) {
            context = new ExecutionContext();
        }

        try {
            boolean isMonitored = shouldMonitorCommand(actionType, runAsInternal);

            // A monitored job is created for monitored external flows
            if (isMonitored || context.isJobRequired()) {
                Job job = createJob(actionType, command);
                JobRepositoryFactory.getJobRepository().saveJob(job);
                context.setExecutionMethod(ExecutionMethod.AsJob);
                context.setJob(job);
                command.setExecutionContext(context);
                context.setMonitored(isMonitored);
            }
        } catch (Exception e) {
            log.errorFormat("Failed to prepare command of type {0} for monitoring due to error {1}",
                    actionType.name(),
                    ExceptionUtils.getMessage(e),
                    e);
        }
    }

    /**
     * Determines if a specific action should be monitored by the following criteria:
     * <ul>
     * <li>{@code VdcActionType.isMonitored} - defined for a specific action type</li>
     * <li>{@code isInternal} - By default, only non-internal commands are monitored</li>
     * </ul>
     *
     * @param actionType
     *            The action type
     * @param isInternal
     *            Indicator of action invocation method
     * @return true if the command should be monitored, else false.
     */
    private static boolean shouldMonitorCommand(VdcActionType actionType, boolean isInternal) {

        return actionType.isActionMonitored() && !isInternal;
    }

    /**
     * Adds a {@link Step} entity by the provided context. A {@link Step} will not be created if<commit_msg>core: Each command represented by a Job should be monitored<commit_after>package org.ovirt.engine.core.bll.job;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.ovirt.engine.core.bll.CommandBase;
import org.ovirt.engine.core.bll.context.CommandContext;
import org.ovirt.engine.core.bll.job.ExecutionContext.ExecutionMethod;
import org.ovirt.engine.core.common.VdcObjectType;
import org.ovirt.engine.core.common.action.VdcActionParametersBase;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.action.VdcReturnValueBase;
import org.ovirt.engine.core.common.job.ExternalSystemType;
import org.ovirt.engine.core.common.job.Job;
import org.ovirt.engine.core.common.job.JobExecutionStatus;
import org.ovirt.engine.core.common.job.Step;
import org.ovirt.engine.core.common.job.StepEnum;
import org.ovirt.engine.core.common.utils.ValidationUtils;
import org.ovirt.engine.core.common.validation.group.PreRun;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.compat.NGuid;
import org.ovirt.engine.core.dal.dbbroker.DbFacade;
import org.ovirt.engine.core.dal.job.ExecutionMessageDirector;
import org.ovirt.engine.core.utils.log.Log;
import org.ovirt.engine.core.utils.log.LogFactory;
import org.ovirt.engine.core.utils.log.LoggedUtils;

/**
 * Provides methods for managing the flow objects the of the command, by the given execution context o the command.
 * <ul>
 * <li>Creates an {@code ExecutionContext} instance for {@code CommandBase}.
 * <li>Creates {@Job} entities.
 * <li>Add {@code Step} to a {@code Job}
 * <li>Add {@code Step} to a {@code Step} as a sub step.
 * <li>End step.
 * <li>End job.
 * <ul>
 */
public class ExecutionHandler {

    private static Log log = LogFactory.getLog(ExecutionHandler.class);

    private static final List<Class<?>> validationGroups = Arrays.asList(new Class<?>[] { PreRun.class });

    /**
     * Creates and returns an instance of {@link Job} entity.
     *
     * @param actionType
     *            The action type the job entity represents.
     * @param command
     *            The {@code CommandBase} instance which the job entity describes.
     * @return An initialized {@code Job} instance.
     */
    public static Job createJob(VdcActionType actionType, CommandBase<?> command) {
        Job job = new Job();

        job.setId(Guid.NewGuid());
        job.setActionType(actionType);
        job.setDescription(ExecutionMessageDirector.resolveJobMessage(actionType, command.getJobMessageProperties()));
        job.setJobSubjectEntities(getSubjectEntities(command.getPermissionCheckSubjects()));
        job.setOwnerId(command.getUserId());
        job.setStatus(JobExecutionStatus.STARTED);
        job.setStartTime(new Date());
        job.setCorrelationId(command.getCorrelationId());

        return job;
    }

    private static Map<Guid, VdcObjectType> getSubjectEntities(Map<Guid, VdcObjectType> subjects) {
        Map<Guid, VdcObjectType> entities = new HashMap<Guid, VdcObjectType>();
        VdcObjectType entityType;
        Guid entityId;
        for (Entry<Guid, VdcObjectType> entry : subjects.entrySet()) {
            entityType = entry.getValue();
            entityId = entry.getKey();
            if (entityType != null && entityId != null) {
                entities.put(entityId, entityType);
            }
        }
        return entities;
    }

    /**
     * Finalizes a {@code Step} execution by a given context in which the step was performed and by the exit status of
     * the step.
     *
     * @param context
     *            The context in which the {@code Step} was executed.
     * @param step
     *            The step to finalize.
     * @param exitStatus
     *            Indicates if the execution described by the step ended successfully or not.
     */
    public static void endStep(ExecutionContext context, Step step, boolean exitStatus) {
        if (context == null) {
            return;
        }
        if (context.isMonitored()) {
            context.setCompleted(true);
            Job job = context.getJob();
            try {
                if (context.getExecutionMethod() == ExecutionMethod.AsJob && job != null) {

                    if (exitStatus) {
                        if (step != null) {
                            step.markStepEnded(exitStatus);
                            JobRepositoryFactory.getJobRepository().updateStep(step);
                        }
                    } else {
                        // step failure will cause the job to be marked as failed
                        job.markJobEnded(false);
                        JobRepositoryFactory.getJobRepository().updateCompletedJobAndSteps(job);
                    }
                } else {
                    Step parentStep = context.getStep();
                    if (context.getExecutionMethod() == ExecutionMethod.AsStep && parentStep != null) {
                        if (exitStatus) {
                            if (step != null) {
                                step.markStepEnded(exitStatus);
                                JobRepositoryFactory.getJobRepository().updateStep(step);
                            }
                        }
                    }
                }
            } catch (Exception e) {
                log.error(e);
            }
        }
    }

    /**
     * Finalizes a {@code Step} execution which represents a VDSM task. In case of a failure status, the job will not be
     * marked as failed at this stage, but via executing the {@code CommandBase.endAction} with the proper status by
     * {@code the AsyncTaskManager}.
     *
     * @param stepId
     *            A unique identifier of the step to finalize.
     * @param exitStatus
     *            The status which the step should be ended with.
     */
    public static void endTaskStep(NGuid stepId, JobExecutionStatus exitStatus) {
        try {
            if (stepId != null) {
                Step step = JobRepositoryFactory.getJobRepository().getStep(stepId.getValue());

                if (step != null) {
                    step.markStepEnded(exitStatus);
                    JobRepositoryFactory.getJobRepository().updateStep(step);
                }
            }
        } catch (Exception e) {
            log.errorFormat("Failed to terminate step {0} with status {1}", stepId, exitStatus, e);
        }
    }

    /**
     * Prepares the monitoring objects for the command by the default behavior:
     * <ul>
     * <li> {@link ExecutionContext} determines how the command should be monitored. By default, non-internal commands
     * will be associated with {@code Job} to represent the command execution. Internal commands will not be monitored
     * by default, therefore the {@code ExecutionContext} is created as non-monitored context.
     * <li> {@link Job} is created for monitored actions
     * </ul>
     *
     * @param command
     *            The created instance of the command (can't be <code>null</code>).
     * @param actionType
     *            The action type of the command
     * @param runAsInternal
     *            Indicates if the command should be run as internal action or not
     */
    public static void prepareCommandForMonitoring(CommandBase<?> command,
            VdcActionType actionType,
            boolean runAsInternal) {

        ExecutionContext context = command.getExecutionContext();
        if (context == null) {
            context = new ExecutionContext();
        }

        try {
            boolean isMonitored = shouldMonitorCommand(actionType, runAsInternal);

            // A monitored job is created for monitored external flows
            if (isMonitored || context.isJobRequired()) {
                Job job = createJob(actionType, command);
                JobRepositoryFactory.getJobRepository().saveJob(job);
                context.setExecutionMethod(ExecutionMethod.AsJob);
                context.setJob(job);
                command.setExecutionContext(context);
                context.setMonitored(true);
            }
        } catch (Exception e) {
            log.errorFormat("Failed to prepare command of type {0} for monitoring due to error {1}",
                    actionType.name(),
                    ExceptionUtils.getMessage(e),
                    e);
        }
    }

    /**
     * Determines if a specific action should be monitored by the following criteria:
     * <ul>
     * <li>{@code VdcActionType.isMonitored} - defined for a specific action type</li>
     * <li>{@code isInternal} - By default, only non-internal commands are monitored</li>
     * </ul>
     *
     * @param actionType
     *            The action type
     * @param isInternal
     *            Indicator of action invocation method
     * @return true if the command should be monitored, else false.
     */
    private static boolean shouldMonitorCommand(VdcActionType actionType, boolean isInternal) {

        return actionType.isActionMonitored() && !isInternal;
    }

    /**
     * Adds a {@link Step} entity by the provided context. A {@link Step} will not be created if
{'fraction_non_alphanumeric': 0.09599871788022865, 'fraction_numerical': 0.0004273732571184358, 'mean_word_length': 2.213182286302781, 'pattern_counts': {'":': 0, '<': 63, '<?xml version=': 0, '>': 63, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2774906', 'n_tokens_mistral': 4974, 'n_tokens_neox': 4836, 'n_words': 1366}