gt:         if (!getTransactive()) {
context: package org.ovirt.engine.core.bll;
  
  import java.io.Serializable;
  import java.lang.reflect.Method;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  import java.util.Map.Entry;
  import java.util.Set;
  
  import javax.ejb.TransactionRolledbackLocalException;
  import javax.transaction.Status;
  import javax.transaction.SystemException;
  import javax.transaction.Transaction;
  
  import org.apache.commons.lang.StringUtils;
  import org.apache.commons.lang.exception.ExceptionUtils;
  import org.ovirt.engine.core.bll.aaa.SessionDataContainer;
  import org.ovirt.engine.core.bll.context.CommandContext;
  import org.ovirt.engine.core.bll.context.CompensationContext;
  import org.ovirt.engine.core.bll.context.DefaultCompensationContext;
  import org.ovirt.engine.core.bll.context.EngineContext;
  import org.ovirt.engine.core.bll.context.NoOpCompensationContext;
  import org.ovirt.engine.core.bll.interfaces.BackendInternal;
  import org.ovirt.engine.core.bll.job.ExecutionContext;
  import org.ovirt.engine.core.bll.job.ExecutionHandler;
  import org.ovirt.engine.core.bll.quota.InvalidQuotaParametersException;
  import org.ovirt.engine.core.bll.quota.QuotaConsumptionParameter;
  import org.ovirt.engine.core.bll.quota.QuotaConsumptionParametersWrapper;
  import org.ovirt.engine.core.bll.quota.QuotaManager;
  import org.ovirt.engine.core.bll.quota.QuotaStorageDependent;
  import org.ovirt.engine.core.bll.quota.QuotaVdsDependent;
  import org.ovirt.engine.core.bll.tasks.CommandCoordinatorUtil;
  import org.ovirt.engine.core.bll.tasks.SPMAsyncTaskHandler;
  import org.ovirt.engine.core.bll.tasks.interfaces.Command;
  import org.ovirt.engine.core.bll.tasks.interfaces.CommandCallBack;
  import org.ovirt.engine.core.bll.tasks.interfaces.SPMTask;
  import org.ovirt.engine.core.bll.utils.BackendUtils;
  import org.ovirt.engine.core.bll.utils.PermissionSubject;
  import org.ovirt.engine.core.common.AuditLogType;
  import org.ovirt.engine.core.common.VdcObjectType;
  import org.ovirt.engine.core.common.action.LockProperties;
  import org.ovirt.engine.core.common.action.LockProperties.Scope;
  import org.ovirt.engine.core.common.action.VdcActionParametersBase;
  import org.ovirt.engine.core.common.action.VdcActionParametersBase.CommandExecutionReason;
  import org.ovirt.engine.core.common.action.VdcActionType;
  import org.ovirt.engine.core.common.action.VdcReturnValueBase;
  import org.ovirt.engine.core.common.asynctasks.AsyncTaskCreationInfo;
  import org.ovirt.engine.core.common.asynctasks.AsyncTaskType;
  import org.ovirt.engine.core.common.businessentities.ActionGroup;
  import org.ovirt.engine.core.common.businessentities.ActionVersionMap;
  import org.ovirt.engine.core.common.businessentities.AsyncTask;
  import org.ovirt.engine.core.common.businessentities.BusinessEntity;
  import org.ovirt.engine.core.common.businessentities.BusinessEntitySnapshot;
  import org.ovirt.engine.core.common.businessentities.BusinessEntitySnapshot.EntityStatusSnapshot;
  import org.ovirt.engine.core.common.businessentities.BusinessEntitySnapshot.SnapshotType;
  import org.ovirt.engine.core.common.businessentities.CommandEntity;
  import org.ovirt.engine.core.common.businessentities.IVdsAsyncCommand;
  import org.ovirt.engine.core.common.businessentities.QuotaEnforcementTypeEnum;
  import org.ovirt.engine.core.common.businessentities.aaa.DbUser;
  import org.ovirt.engine.core.common.errors.VdcBLLException;
  import org.ovirt.engine.core.common.errors.VdcBllErrors;
  import org.ovirt.engine.core.common.errors.VdcBllMessages;
  import org.ovirt.engine.core.common.errors.VdcFault;
  import org.ovirt.engine.core.common.job.Step;
  import org.ovirt.engine.core.common.job.StepEnum;
  import org.ovirt.engine.core.common.queries.VdcQueryParametersBase;
  import org.ovirt.engine.core.common.queries.VdcQueryReturnValue;
  import org.ovirt.engine.core.common.queries.VdcQueryType;
  import org.ovirt.engine.core.common.utils.Pair;
  import org.ovirt.engine.core.common.utils.ValidationUtils;
  import org.ovirt.engine.core.common.vdscommands.VDSCommandType;
  import org.ovirt.engine.core.common.vdscommands.VDSParametersBase;
  import org.ovirt.engine.core.common.vdscommands.VDSReturnValue;
  import org.ovirt.engine.core.compat.CommandStatus;
  import org.ovirt.engine.core.compat.Guid;
  import org.ovirt.engine.core.compat.TransactionScopeOption;
  import org.ovirt.engine.core.compat.Version;
  import org.ovirt.engine.core.dal.dbbroker.DbFacade;
  import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogDirector;
  import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogableBase;
  import org.ovirt.engine.core.dal.job.ExecutionMessageDirector;
  import org.ovirt.engine.core.dao.BusinessEntitySnapshotDAO;
  import org.ovirt.engine.core.dao.GenericDao;
  import org.ovirt.engine.core.dao.StatusAwareDao;
  import org.ovirt.engine.core.dao.VdsSpmIdMapDAO;
  import org.ovirt.engine.core.utils.Deserializer;
  import org.ovirt.engine.core.utils.ReflectionUtils;
  import org.ovirt.engine.core.utils.SerializationFactory;
  import org.ovirt.engine.core.utils.ThreadLocalParamsContainer;
  import org.ovirt.engine.core.utils.lock.EngineLock;
  import org.ovirt.engine.core.utils.lock.LockManager;
  import org.ovirt.engine.core.utils.lock.LockManagerFactory;
  import org.ovirt.engine.core.utils.log.Log;
  import org.ovirt.engine.core.utils.log.LogFactory;
  import org.ovirt.engine.core.utils.transaction.RollbackHandler;
  import org.ovirt.engine.core.utils.transaction.TransactionMethod;
  import org.ovirt.engine.core.utils.transaction.TransactionSupport;
  import org.springframework.dao.DataAccessException;
  
  public abstract class CommandBase<T extends VdcActionParametersBase> extends AuditLogableBase implements
          RollbackHandler, TransactionMethod<Object>, Command<T> {
  
      /* Multiplier used to convert GB to bytes or vice versa. */
      protected static final long BYTES_IN_GB = 1024 * 1024 * 1024;
      private static final String DEFAULT_TASK_KEY = "DEFAULT_TASK_KEY";
      private T _parameters;
      private VdcReturnValueBase _returnValue;
      private CommandActionState _actionState = CommandActionState.EXECUTE;
      private VdcActionType actionType;
      private final List<Class<?>> validationGroups = new ArrayList<Class<?>>();
      private final Guid commandId;
      private boolean quotaChanged = false;
      private String _description = "";
      private TransactionScopeOption scope;
      private TransactionScopeOption endActionScope;
      private List<QuotaConsumptionParameter> consumptionParameters;
      /** Indicates whether the acquired locks should be released after the execute method or not */
      private boolean releaseLocksAtEndOfExecute = true;
      /** Object which is representing a lock that some commands will acquire */
  
      protected Log log = LogFactory.getLog(getClass());
  
      /** The context defines how to monitor the command and handle its compensation */
      private final CommandContext context;
  
      /** A map contains the properties for describing the job */
      protected Map<String, String> jobProperties;
  
      /** Handlers for performing the logical parts of the command */
      private List<SPMAsyncTaskHandler> taskHandlers;
  
      private Map<Guid, CommandBase<?>> childCommandsMap = new HashMap<>();
      private Map<Guid, Pair<VdcActionType, VdcActionParametersBase>> childCommandInfoMap = new HashMap<>();
      private CommandStatus commandStatus = CommandStatus.NOT_STARTED;
  
      public void addChildCommandInfo(Guid id, VdcActionType vdcActionType, VdcActionParametersBase parameters) {
          childCommandInfoMap.put(id, new Pair<VdcActionType, VdcActionParametersBase>(vdcActionType, parameters));
      }
  
      protected List<VdcReturnValueBase> executeChildCommands() {
          List<VdcReturnValueBase> results = new ArrayList<>(childCommandsMap.size());
          for (Entry<Guid, CommandBase<?>> entry : childCommandsMap.entrySet()) {
              results.add(executeChildCommand(entry.getKey()));
          }
          return results;
      }
  
      protected VdcReturnValueBase executeChildCommand(Guid idInCommandsMap) {
          CommandBase<?> command = childCommandsMap.get(idInCommandsMap);
          return BackendUtils.getBackendCommandObjectsHandler(log).runAction(command, getExecutionContext());
      }
  
  
      protected CommandActionState getActionState() {
          return _actionState;
      }
  
      protected CommandBase() {
          this(Guid.newGuid());
      }
  
      protected CommandBase(T parameters) {
          this(parameters, null);
      }
  
      protected CommandBase(T parameters, CommandContext cmdContext) {
          if (cmdContext == null) {
              cmdContext =
                      new CommandContext(new EngineContext().withSessionId(parameters.getSessionId()))
                              .withExecutionContext(new ExecutionContext());
          } else if (cmdContext.getEngineContext().getSessionId() == null) {
              // Needed for SEAT mechanism - session ID is available only on parameters
              // upon command re-instantiation (when moving between task handlers).
              cmdContext.getEngineContext().withSessionId(parameters.getSessionId());
          }
          this.context = cmdContext;
          _parameters = parameters;
          DbUser user =
                  SessionDataContainer.getInstance().getUser(cmdContext.getEngineContext().getSessionId(), true);
          if (user != null) {
              setCurrentUser(user);
          }
          setUserName(SessionDataContainer.getInstance().getUserName(cmdContext.getEngineContext().getSessionId()));
          ExecutionContext executionContext = cmdContext.getExecutionContext();
          if (executionContext.getJob() != null) {
              setJobId(executionContext.getJob().getId());
          } else if (executionContext.getStep() != null) {
              setJobId(executionContext.getStep().getJobId());
          }
  
          Guid commandIdFromParameters = parameters.getCommandId();
          if (commandIdFromParameters == null) {
              commandIdFromParameters = Guid.newGuid();
              getParameters().setCommandId(commandIdFromParameters);
          }
  
          commandId = commandIdFromParameters;
          taskHandlers = initTaskHandlers();
          setCorrelationId(parameters.getCorrelationId());
      }
  
      /**
       * Constructor for command creation when compensation is applied on startup
       *
       * @param commandId
       */
      protected CommandBase(Guid commandId) {
          this.context = new CommandContext(new EngineContext());
          this.commandId = commandId;
      }
  
      protected List<SPMAsyncTaskHandler> initTaskHandlers() {
          return null;
      }
  
      /**
       * Checks if possible to perform rollback using command, and if so performs it
       *
       * @param commandType
       *            command type for the rollback
       * @param params
       *            parameters for the rollback
       * @param context
       *            command context for the rollback
       * @return result of the command execution
       */
      protected VdcReturnValueBase attemptRollback(VdcActionType commandType,
              VdcActionParametersBase params) {
          if (canPerformRollbackUsingCommand(commandType, params)) {
              params.setExecutionReason(CommandExecutionReason.ROLLBACK_FLOW);
              params.setTransactionScopeOption(TransactionScopeOption.RequiresNew);
              return getBackend().runInternalAction(commandType, params, context.clone());
          }
          return new VdcReturnValueBase();
      }
  
      protected BackendInternal getBackend() {
          return Backend.getInstance();
      }
  
      /**
       * Checks if possible to perform rollback using command, and if so performs it
       *
       * @param commandType
       *            command type for the rollback
       * @param params
       *            parameters for the rollback
       * @return result of the command execution
       */
      protected VdcReturnValueBase checkAndPerformRollbackUsingCommand(VdcActionType commandType,
              VdcActionParametersBase params) {
          return attemptRollback(commandType, params);
      }
  
      /**
       * Checks if it is possible to rollback the command using a command (and not VDSM)
       *
       * @param commandType
       *            the rollback command to be executed
       * @param params
       *            parameters for the rollback command
       * @return true if it is possible to run rollback using command
       */
      protected boolean canPerformRollbackUsingCommand
              (VdcActionType commandType,
                      VdcActionParametersBase params) {
          return true;
      }
  
      /**
       * Create an appropriate compensation context. The default is one that does nothing for command that don't run in a
       * transaction, and a real one for commands that run in a transaction.
       *
       * @param transactionScopeOption
       *            The transaction scope.
       * @param forceCompensation
       * @return The compensation context to use.
       */
      private CompensationContext createCompensationContext(TransactionScopeOption transactionScopeOption,
              boolean forceCompensation) {
          if (transactionScopeOption == TransactionScopeOption.Suppress && !forceCompensation) {
              return NoOpCompensationContext.getInstance();
          }
  
          return createDefaultCompensationContext(commandId);
      }
  
      protected DefaultCompensationContext createDefaultCompensationContext(Guid commandId) {
          DefaultCompensationContext defaultContext = new DefaultCompensationContext();
          defaultContext.setCommandId(commandId);
          defaultContext.setCommandType(getClass().getName());
          defaultContext.setBusinessEntitySnapshotDAO(getBusinessEntitySnapshotDAO());
          defaultContext.setSnapshotSerializer(
                  SerializationFactory.getSerializer());
          return defaultContext;
      }
  
      protected BusinessEntitySnapshotDAO getBusinessEntitySnapshotDAO() {
          return DbFacade.getInstance().getBusinessEntitySnapshotDao();
      }
  
      protected VdsSpmIdMapDAO getVdsSpmIdMapDAO() {
          return DbFacade.getInstance().getVdsSpmIdMapDao();
      }
  
      /**
       * @return the compensationContext
       */
      public CompensationContext getCompensationContext() {
          return context.getCompensationContext();
      }
  
      /**
       * @param compensationContext the compensationContext to set
       */
      public void setCompensationContext(CompensationContext compensationContext) {
          context.withCompensationContext(compensationContext);
      }
  
      public VdcReturnValueBase canDoActionOnly() {
          setActionMessageParameters();
          getReturnValue().setCanDoAction(internalCanDoAction());
          String tempVar = getDescription();
          getReturnValue().setDescription((tempVar != null) ? tempVar : getReturnValue().getDescription());
          return _returnValue;
      }
  
      public VdcReturnValueBase executeAction() {
          determineExecutionReason();
          _actionState = CommandActionState.EXECUTE;
          String tempVar = getDescription();
          getReturnValue().setDescription((tempVar != null) ? tempVar : getReturnValue().getDescription());
          setActionMessageParameters();
          Step validatingStep=null;
          boolean actionAllowed = false;
          boolean isExternal = this.getParameters().getJobId() != null || this.getParameters().getStepId() != null;
          if (!isExternal) {
              validatingStep = ExecutionHandler.addStep(getExecutionContext(), StepEnum.VALIDATING, null);
          }
  
          try {
              actionAllowed = getReturnValue().getCanDoAction() || internalCanDoAction();
              if (!isExternal) {
                  ExecutionHandler.endStep(getExecutionContext(), validatingStep, actionAllowed);
              }
  
              if (actionAllowed) {
                  execute();
              } else {
                  getReturnValue().setCanDoAction(false);
                  clearChildAsyncTasksWithOutVdsmId();
              }
          } finally {
              freeLockExecute();
              clearAsyncTasksWithOutVdsmId();
          }
          return getReturnValue();
      }
  
      private void clearChildAsyncTasksWithOutVdsmId() {
          for (Entry<Guid, CommandBase<?>> entry : childCommandsMap.entrySet()) {
              entry.getValue().clearAsyncTasksWithOutVdsmId();
              entry.getValue().clearChildAsyncTasksWithOutVdsmId();
          }
      }
  
      private void clearAsyncTasksWithOutVdsmId() {
          if (!getReturnValue().getTaskPlaceHolderIdList().isEmpty()) {
              TransactionSupport.executeInNewTransaction(new TransactionMethod<Void>() {
                  @Override
                  public Void runInTransaction() {
                      for (Guid asyncTaskId : getReturnValue().getTaskPlaceHolderIdList()) {
                          AsyncTask task = CommandCoordinatorUtil.getAsyncTaskFromDb(asyncTaskId);
                          if (task != null && Guid.isNullOrEmpty(task.getVdsmTaskId())) {
                              CommandCoordinatorUtil.removeTaskFromDbByTaskId(task.getTaskId());
                          }
  
                      }
                      return null;
                  }
              });
          }
      }
  
      private void determineExecutionReason() {
          if (getParameters().getExecutionReason() == null) {
              getParameters().setExecutionReason(CommandExecutionReason.REGULAR_FLOW);
          }
      }
  
      /**
       * Run the default compensation logic (inside a new transaction):<br>
       * <ol>
       * <li>Get all the entity snapshots that this command has created.</li>
       * <li>For each snapshot:</li>
       * <ol>
       * <li>Deserialize the entity.</li>
       * <li>Using the entity DAO:</li>
       * <ul>
       * <li>If the entity was added by the command, remove it.</li>
       * <li>Otherwise, If the entity is not in DB anymore, restore it.</li>
       * <li>Otherwise, update it.</li>
       * </ul>
       * </ol>
       * <li>Remove all the snapshots for this command, since we handled them.</li> </ol>
       */
      protected void compensate() {
          if (hasTaskHandlers()) {
              getParameters().setExecutionReason(CommandExecutionReason.ROLLBACK_FLOW);
              getCurrentTaskHandler().compensate();
              revertPreviousHandlers();
          } else {
              internalCompensate();
          }
      }
  
      @SuppressWarnings({ "unchecked", "synthetic-access" })
      protected final void internalCompensate() {
          try {
              if (isQuotaDependant()) {
                  rollbackQuota();
              }
          } catch (NullPointerException e) {
              log.debug("RollbackQuota: failed (may be because quota is disabled)", e);
          }
          TransactionSupport.executeInNewTransaction(new TransactionMethod<Object>() {
              @Override
              public Object runInTransaction() {
                  Deserializer deserializer =
                          SerializationFactory.getDeserializer();
                  List<BusinessEntitySnapshot> entitySnapshots =
                          getBusinessEntitySnapshotDAO().getAllForCommandId(commandId);
                  log.debugFormat("Command [id={0}]: {1} compensation data.", commandId,
                          entitySnapshots.isEmpty() ? "No" : "Going over");
                  for (BusinessEntitySnapshot snapshot : entitySnapshots) {
                      Class<Serializable> snapshotClass =
                              (Class<Serializable>) ReflectionUtils.getClassFor(snapshot.getSnapshotClass());
                      Serializable snapshotData = deserializer.deserialize(snapshot.getEntitySnapshot(), snapshotClass);
                      log.infoFormat("Command [id={0}]: Compensating {1} of {2}; snapshot: {3}.",
                              commandId,
                              snapshot.getSnapshotType(),
                              snapshot.getEntityType(),
                              (snapshot.getSnapshotType() == SnapshotType.DELETED_OR_UPDATED_ENTITY ? "id=" + snapshot.getEntityId()
                                      : snapshotData.toString()));
                      Class<BusinessEntity<Serializable>> entityClass =
                              (Class<BusinessEntity<Serializable>>) ReflectionUtils.getClassFor(snapshot.getEntityType());
                      GenericDao<BusinessEntity<Serializable>, Serializable> daoForEntity =
                              DbFacade.getInstance().getDaoForEntity(entityClass);
  
                      switch (snapshot.getSnapshotType()) {
                      case CHANGED_STATUS_ONLY:
                          EntityStatusSnapshot entityStatusSnapshot = (EntityStatusSnapshot) snapshotData;
                          ((StatusAwareDao<Serializable, Enum<?>>) daoForEntity).updateStatus(
                                  entityStatusSnapshot.getId(), entityStatusSnapshot.getStatus());
                          break;
                      case DELETED_OR_UPDATED_ENTITY:
                          BusinessEntity<Serializable> entitySnapshot = (BusinessEntity<Serializable>) snapshotData;
                          if (daoForEntity.get(entitySnapshot.getId()) == null) {
                              daoForEntity.save(entitySnapshot);
                          } else {
                              daoForEntity.update(entitySnapshot);
                          }
                          break;
                      case UPDATED_ONLY_ENTITY:
                          daoForEntity.update((BusinessEntity<Serializable>)snapshotData);
                          break;
                      case NEW_ENTITY_ID:
                          daoForEntity.remove(snapshotData);
                          break;
                      }
                  }
  
                  cleanUpCompensationData();
                  return null;
              }
  
          });
      }
  
      /**
       * Delete the compensation data, so that we don't accidentaly try to compensate it at a later time.
       */
      private void cleanUpCompensationData() {
          if (!(getCompensationContext() instanceof NoOpCompensationContext)) {
              getBusinessEntitySnapshotDAO().removeAllForCommandId(commandId);
          }
      }
  
      protected void startFinalizingStep() {
          ExecutionHandler.startFinalizingStep(getExecutionContext());
      }
  
      public VdcReturnValueBase endAction() {
          if (!hasTaskHandlers() || getExecutionIndex() == getTaskHandlers().size() - 1) {
              startFinalizingStep();
          }
  
          try {
              initiateLockEndAction();
              setActionState();
              handleTransactivity();
              TransactionSupport.executeInScope(endActionScope, this);
          } catch (TransactionRolledbackLocalException e) {
              log.infoFormat("endAction: Transaction was aborted in {0}", this.getClass().getName());
          } finally {
              freeLockEndAction();
              if (getCommandShouldBeLogged()) {
                  logCommand();
              }
          }
  
          return getReturnValue();
      }
  
      /**
       * The following method should initiate a lock , in order to release it at endAction()
       */
      private void initiateLockEndAction() {
          if (context.getLock() == null) {
              LockProperties lockProperties = getLockProperties();
              if (Scope.Command.equals(lockProperties.getScope())) {
                  context.withLock(buildLock());
              }
  
          }
      }
  
      private void handleTransactivity() {
          scope =
                  (getParameters() != null) ? getParameters().getTransactionScopeOption()
                          : TransactionScopeOption.Required;
          endActionScope = scope;
          boolean forceCompensation = getForceCompensation();
          // @NonTransactiveAttribute annotation overrides the scope passed by the
          // command parameters
  