All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.activiti.engine.impl.asyncexecutor.DefaultJobManager Maven / Gradle / Ivy

package org.activiti.engine.impl.asyncexecutor;

import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;

import org.activiti.bpmn.model.Event;
import org.activiti.bpmn.model.EventDefinition;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.TimerEventDefinition;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.ActivitiIllegalArgumentException;
import org.activiti.engine.delegate.Expression;
import org.activiti.engine.delegate.VariableScope;
import org.activiti.engine.delegate.event.ActivitiEventDispatcher;
import org.activiti.engine.delegate.event.ActivitiEventType;
import org.activiti.engine.delegate.event.impl.ActivitiEventBuilder;
import org.activiti.engine.impl.calendar.BusinessCalendar;
import org.activiti.engine.impl.calendar.CycleBusinessCalendar;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.el.NoExecutionVariableScope;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.jobexecutor.AsyncContinuationJobHandler;
import org.activiti.engine.impl.jobexecutor.AsyncJobAddedNotification;
import org.activiti.engine.impl.jobexecutor.JobHandler;
import org.activiti.engine.impl.jobexecutor.TimerEventHandler;
import org.activiti.engine.impl.jobexecutor.TimerStartEventJobHandler;
import org.activiti.engine.impl.jobexecutor.TriggerTimerEventJobHandler;
import org.activiti.engine.impl.persistence.entity.AbstractJobEntity;
import org.activiti.engine.impl.persistence.entity.DeadLetterJobEntity;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.ExecutionEntityManager;
import org.activiti.engine.impl.persistence.entity.JobEntity;
import org.activiti.engine.impl.persistence.entity.SuspendedJobEntity;
import org.activiti.engine.impl.persistence.entity.TimerJobEntity;
import org.activiti.engine.impl.persistence.entity.TimerJobEntityManager;
import org.activiti.engine.impl.util.ProcessDefinitionUtil;
import org.activiti.engine.impl.util.TimerUtil;
import org.activiti.engine.runtime.Job;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DefaultJobManager implements JobManager {
  
  private static Logger logger = LoggerFactory.getLogger(DefaultJobManager.class);
  
  protected ProcessEngineConfigurationImpl processEngineConfiguration;
  
  public DefaultJobManager() {
  }
  
  public DefaultJobManager(ProcessEngineConfigurationImpl processEngineConfiguration) {
    this.processEngineConfiguration = processEngineConfiguration;
  }
  
  @Override
  public JobEntity createAsyncJob(ExecutionEntity execution, boolean exclusive) {
    JobEntity jobEntity = null;
    // When the async executor is activated, the job is directly passed on to the async executor thread
    if (isAsyncExecutorActive()) {
      jobEntity = internalCreateLockedAsyncJob(execution, exclusive);
      
    } else {
      jobEntity = internalCreateAsyncJob(execution, exclusive);
    }
    
    return jobEntity;
  }

  @Override
  public void scheduleAsyncJob(JobEntity jobEntity) {
    processEngineConfiguration.getJobEntityManager().insert(jobEntity);
    triggerExecutorIfNeeded(jobEntity);
  }

  protected void triggerExecutorIfNeeded(JobEntity jobEntity) {
    // When the async executor is activated, the job is directly passed on to the async executor thread
    if (isAsyncExecutorActive()) {
      hintAsyncExecutor(jobEntity); 
    }
  }
  
  @Override
  public TimerJobEntity createTimerJob(TimerEventDefinition timerEventDefinition, boolean interrupting, 
      ExecutionEntity execution, String timerEventType, String jobHandlerConfiguration) {
    
    TimerJobEntity timerEntity = TimerUtil.createTimerEntityForTimerEventDefinition(timerEventDefinition, interrupting, 
        execution, timerEventType, jobHandlerConfiguration);
    
    return timerEntity;
  }
  
  @Override
  public void scheduleTimerJob(TimerJobEntity timerJob) {
    if (timerJob == null) {
      throw new ActivitiException("Empty timer job can not be scheduled");
    }
    
    processEngineConfiguration.getTimerJobEntityManager().insert(timerJob);

    CommandContext commandContext = Context.getCommandContext();
    ActivitiEventDispatcher eventDispatcher = commandContext.getEventDispatcher();
    if (eventDispatcher.isEnabled()) {
      eventDispatcher.dispatchEvent(ActivitiEventBuilder.createEntityEvent(ActivitiEventType.TIMER_SCHEDULED, timerJob));
    }
  }
  
  @Override
  public JobEntity moveTimerJobToExecutableJob(TimerJobEntity timerJob) {
    if (timerJob == null) {
      throw new ActivitiException("Empty timer job can not be scheduled");
    }
    
    JobEntity executableJob = createExecutableJobFromOtherJob(timerJob);
    boolean insertSuccesful = processEngineConfiguration.getJobEntityManager().insertJobEntity(executableJob);
    if (insertSuccesful) {
      processEngineConfiguration.getTimerJobEntityManager().delete(timerJob);
      triggerExecutorIfNeeded(executableJob);
      return executableJob;
    }
    return null;
  }
  
  @Override
  public TimerJobEntity moveJobToTimerJob(AbstractJobEntity job) {
    TimerJobEntity timerJob = createTimerJobFromOtherJob(job);
    boolean insertSuccesful = processEngineConfiguration.getTimerJobEntityManager().insertTimerJobEntity(timerJob);
    if (insertSuccesful) {
      if (job instanceof JobEntity) {
        processEngineConfiguration.getJobEntityManager().delete((JobEntity) job);
      } else if (job instanceof SuspendedJobEntity) {
        processEngineConfiguration.getSuspendedJobEntityManager().delete((SuspendedJobEntity) job);
      }
      
      return timerJob;
    }
    return null;
  }
  
  @Override
  public SuspendedJobEntity moveJobToSuspendedJob(AbstractJobEntity job) {
    SuspendedJobEntity suspendedJob = createSuspendedJobFromOtherJob(job);
    processEngineConfiguration.getSuspendedJobEntityManager().insert(suspendedJob);
    if (job instanceof TimerJobEntity) {
      processEngineConfiguration.getTimerJobEntityManager().delete((TimerJobEntity) job);
      
    } else if (job instanceof JobEntity) {
      processEngineConfiguration.getJobEntityManager().delete((JobEntity) job);
    }
    
    return suspendedJob;
  }
  
  @Override
  public AbstractJobEntity activateSuspendedJob(SuspendedJobEntity job) {
    AbstractJobEntity activatedJob = null;
    if (Job.JOB_TYPE_TIMER.equals(job.getJobType())) {
      activatedJob = createTimerJobFromOtherJob(job);
      processEngineConfiguration.getTimerJobEntityManager().insert((TimerJobEntity) activatedJob);
      
    } else {
      activatedJob = createExecutableJobFromOtherJob(job);
      JobEntity jobEntity = (JobEntity) activatedJob;
      processEngineConfiguration.getJobEntityManager().insert(jobEntity);
      triggerExecutorIfNeeded(jobEntity);
    }
    
    processEngineConfiguration.getSuspendedJobEntityManager().delete(job);
    return activatedJob;
  }
  
  @Override
  public DeadLetterJobEntity moveJobToDeadLetterJob(AbstractJobEntity job) {
    DeadLetterJobEntity deadLetterJob = createDeadLetterJobFromOtherJob(job);
    processEngineConfiguration.getDeadLetterJobEntityManager().insert(deadLetterJob);
    if (job instanceof TimerJobEntity) {
      processEngineConfiguration.getTimerJobEntityManager().delete((TimerJobEntity) job);
      
    } else if (job instanceof JobEntity) {
      processEngineConfiguration.getJobEntityManager().delete((JobEntity) job);
    }
    
    return deadLetterJob;
  }
  
  @Override
  public JobEntity moveDeadLetterJobToExecutableJob(DeadLetterJobEntity deadLetterJobEntity, int retries) {
    if (deadLetterJobEntity == null) {
      throw new ActivitiIllegalArgumentException("Null job provided");
    }
    
    JobEntity executableJob = createExecutableJobFromOtherJob(deadLetterJobEntity);
    executableJob.setRetries(retries);
    boolean insertSuccesful = processEngineConfiguration.getJobEntityManager().insertJobEntity(executableJob);
    if (insertSuccesful) {
      processEngineConfiguration.getDeadLetterJobEntityManager().delete(deadLetterJobEntity);
      triggerExecutorIfNeeded(executableJob);
      return executableJob;
    }
    return null;
  }
  
  @Override
  public void execute(Job job) {
    if (job instanceof JobEntity) {
      if (Job.JOB_TYPE_MESSAGE.equals(job.getJobType())) {
        executeMessageJob((JobEntity) job);
      } else if (Job.JOB_TYPE_TIMER.equals(job.getJobType())) {
        executeTimerJob((JobEntity) job);
      }
      
    } else {
      throw new ActivitiException("Only jobs with type JobEntity are supported to be executed");
    }
  }
  
  @Override
  public void unacquire(Job job) {
    
    // Deleting the old job and inserting it again with another id,
    // will avoid that the job is immediately is picked up again (for example
    // when doing lots of exclusive jobs for the same process instance)
    if (job instanceof JobEntity) {
      JobEntity jobEntity = (JobEntity) job;
      processEngineConfiguration.getJobEntityManager().delete(jobEntity.getId());
      
      JobEntity newJobEntity = processEngineConfiguration.getJobEntityManager().create();
      copyJobInfo(newJobEntity, jobEntity);
      newJobEntity.setId(null); // We want a new id to be assigned to this job
      newJobEntity.setLockExpirationTime(null);
      newJobEntity.setLockOwner(null);
      processEngineConfiguration.getJobEntityManager().insert(newJobEntity);
      
      // We're not calling triggerExecutorIfNeeded here after the inser. The unacquire happened
      // for a reason (eg queue full or exclusive lock failure). No need to try it immediately again,
      // as the chance of failure will be high.
      
    } else {
      // It could be a v5 job, so simply unlock it.
      processEngineConfiguration.getJobEntityManager().resetExpiredJob(job.getId());
    }
    
  }
   
  protected void executeMessageJob(JobEntity jobEntity) {
    executeJobHandler(jobEntity);
    if (jobEntity.getId() != null) {
      Context.getCommandContext().getJobEntityManager().delete(jobEntity);
    }
  }
   
  protected void executeTimerJob(JobEntity timerEntity) {
    TimerJobEntityManager timerJobEntityManager = processEngineConfiguration.getTimerJobEntityManager();
    
    VariableScope variableScope = null;
    if (timerEntity.getExecutionId() != null) {
      variableScope = getExecutionEntityManager().findById(timerEntity.getExecutionId());
    }
     
    if (variableScope == null) {
      variableScope = NoExecutionVariableScope.getSharedInstance();
    }

    // set endDate if it was set to the definition
    restoreExtraData(timerEntity, variableScope);

    if (timerEntity.getDuedate() != null && !isValidTime(timerEntity, timerEntity.getDuedate(), variableScope)) {
      if (logger.isDebugEnabled()) {
        logger.debug("Timer {} fired. but the dueDate is after the endDate.  Deleting timer.", timerEntity.getId());
      }
      processEngineConfiguration.getJobEntityManager().delete(timerEntity);
      return;
    }

    executeJobHandler(timerEntity);
    processEngineConfiguration.getJobEntityManager().delete(timerEntity);

    if (logger.isDebugEnabled()) {
      logger.debug("Timer {} fired. Deleting timer.", timerEntity.getId());
    }
    
    if (timerEntity.getRepeat() != null) {
      TimerJobEntity newTimerJobEntity = timerJobEntityManager.createAndCalculateNextTimer(timerEntity, variableScope);
      if (newTimerJobEntity != null) {
        scheduleTimerJob(newTimerJobEntity);
      }
    }
  }
  
  protected void executeJobHandler(JobEntity jobEntity) {
    ExecutionEntity execution = null;
    if (jobEntity.getExecutionId() != null) {
      execution = getExecutionEntityManager().findById(jobEntity.getExecutionId());
    }

    Map jobHandlers = processEngineConfiguration.getJobHandlers();
    JobHandler jobHandler = jobHandlers.get(jobEntity.getJobHandlerType());
    jobHandler.execute(jobEntity, jobEntity.getJobHandlerConfiguration(), execution, getCommandContext());
  }
   
  protected void restoreExtraData(JobEntity timerEntity, VariableScope variableScope) {
    String activityId = timerEntity.getJobHandlerConfiguration();

    if (timerEntity.getJobHandlerType().equalsIgnoreCase(TimerStartEventJobHandler.TYPE) ||
        timerEntity.getJobHandlerType().equalsIgnoreCase(TriggerTimerEventJobHandler.TYPE)) {

      activityId = TimerEventHandler.getActivityIdFromConfiguration(timerEntity.getJobHandlerConfiguration());
      String endDateExpressionString = TimerEventHandler.getEndDateFromConfiguration(timerEntity.getJobHandlerConfiguration());

      if (endDateExpressionString != null) {
        Expression endDateExpression = processEngineConfiguration.getExpressionManager().createExpression(endDateExpressionString);

        String endDateString = null;

        BusinessCalendar businessCalendar = processEngineConfiguration.getBusinessCalendarManager().getBusinessCalendar(
            getBusinessCalendarName(TimerEventHandler.geCalendarNameFromConfiguration(timerEntity.getJobHandlerConfiguration()), variableScope));

        if (endDateExpression != null) {
          Object endDateValue = endDateExpression.getValue(variableScope);
          if (endDateValue instanceof String) {
            endDateString = (String) endDateValue;
          } else if (endDateValue instanceof Date) {
            timerEntity.setEndDate((Date) endDateValue);
          } else {
            throw new ActivitiException("Timer '" + ((ExecutionEntity) variableScope).getActivityId()
                + "' was not configured with a valid duration/time, either hand in a java.util.Date or a String in format 'yyyy-MM-dd'T'hh:mm:ss'");
          }

          if (timerEntity.getEndDate() == null) {
            timerEntity.setEndDate(businessCalendar.resolveEndDate(endDateString));
          }
        }
      }
    }

    int maxIterations = 1;
    if (timerEntity.getProcessDefinitionId() != null) {
      org.activiti.bpmn.model.Process process = ProcessDefinitionUtil.getProcess(timerEntity.getProcessDefinitionId());
      maxIterations = getMaxIterations(process, activityId);
      if (maxIterations <= 1) {
        maxIterations = getMaxIterations(process, activityId);
      }
    }
    timerEntity.setMaxIterations(maxIterations);
  }
   
  protected int getMaxIterations(org.activiti.bpmn.model.Process process, String activityId) {
    FlowElement flowElement = process.getFlowElement(activityId, true);
    if (flowElement != null) {
      if (flowElement instanceof Event) {
         
        Event event = (Event) flowElement;
        List eventDefinitions = event.getEventDefinitions();
         
        if (eventDefinitions != null) {
           
          for (EventDefinition eventDefinition : eventDefinitions) {
            if (eventDefinition instanceof TimerEventDefinition) {
              TimerEventDefinition timerEventDefinition = (TimerEventDefinition) eventDefinition;
              if (timerEventDefinition.getTimeCycle() != null) {
                return calculateMaxIterationsValue(timerEventDefinition.getTimeCycle());
              }
            }
          }
           
        }
         
      }
    }
    return -1;
  }
   
  protected int calculateMaxIterationsValue(String originalExpression) {
    int times = Integer.MAX_VALUE;
    List expression = Arrays.asList(originalExpression.split("/"));
    if (expression.size() > 1 && expression.get(0).startsWith("R")) {
      times = Integer.MAX_VALUE;
      if (expression.get(0).length() > 1) {
        times = Integer.parseInt(expression.get(0).substring(1));
      }
    }
    return times;
  }
   
  protected boolean isValidTime(JobEntity timerEntity, Date newTimerDate, VariableScope variableScope) {
    BusinessCalendar businessCalendar = processEngineConfiguration.getBusinessCalendarManager().getBusinessCalendar(
        getBusinessCalendarName(TimerEventHandler.geCalendarNameFromConfiguration(timerEntity.getJobHandlerConfiguration()), variableScope));
    return businessCalendar.validateDuedate(timerEntity.getRepeat(), timerEntity.getMaxIterations(), timerEntity.getEndDate(), newTimerDate);
  }
  
  protected String getBusinessCalendarName(String calendarName, VariableScope variableScope) {
    String businessCalendarName = CycleBusinessCalendar.NAME;
    if (StringUtils.isNotEmpty(calendarName)) {
      businessCalendarName = (String) Context.getProcessEngineConfiguration().getExpressionManager()
          .createExpression(calendarName).getValue(variableScope);
    }
    return businessCalendarName;
  }
  
  protected void hintAsyncExecutor(JobEntity job) {
    AsyncJobAddedNotification jobAddedNotification = new AsyncJobAddedNotification(job, getAsyncExecutor());
    getCommandContext().addCloseListener(jobAddedNotification);
  }
  
  protected JobEntity internalCreateAsyncJob(ExecutionEntity execution, boolean exclusive) {
    JobEntity asyncJob = processEngineConfiguration.getJobEntityManager().create();
    fillDefaultAsyncJobInfo(asyncJob, execution, exclusive);
    return asyncJob;
  }
  
  protected JobEntity internalCreateLockedAsyncJob(ExecutionEntity execution, boolean exclusive) {
    JobEntity asyncJob = processEngineConfiguration.getJobEntityManager().create();
    fillDefaultAsyncJobInfo(asyncJob, execution, exclusive);
    
    GregorianCalendar gregorianCalendar = new GregorianCalendar();
    gregorianCalendar.setTime(processEngineConfiguration.getClock().getCurrentTime());
    gregorianCalendar.add(Calendar.MILLISECOND, getAsyncExecutor().getAsyncJobLockTimeInMillis());
    asyncJob.setLockExpirationTime(gregorianCalendar.getTime());
    asyncJob.setLockOwner(getAsyncExecutor().getLockOwner());
    
    return asyncJob;
  }
  
  protected void fillDefaultAsyncJobInfo(JobEntity jobEntity, ExecutionEntity execution, boolean exclusive) {
    jobEntity.setJobType(JobEntity.JOB_TYPE_MESSAGE);
    jobEntity.setRevision(1);
    jobEntity.setRetries(processEngineConfiguration.getAsyncExecutorNumberOfRetries());
    jobEntity.setExecutionId(execution.getId());
    jobEntity.setProcessInstanceId(execution.getProcessInstanceId());
    jobEntity.setProcessDefinitionId(execution.getProcessDefinitionId());
    jobEntity.setExclusive(exclusive);
    jobEntity.setJobHandlerType(AsyncContinuationJobHandler.TYPE);
    
    // Inherit tenant id (if applicable)
    if (execution.getTenantId() != null) {
      jobEntity.setTenantId(execution.getTenantId());
    }
  }
  
  protected JobEntity createExecutableJobFromOtherJob(AbstractJobEntity job) {
    JobEntity executableJob = processEngineConfiguration.getJobEntityManager().create();
    copyJobInfo(executableJob, job);
    
    if (isAsyncExecutorActive()) {
      GregorianCalendar gregorianCalendar = new GregorianCalendar();
      gregorianCalendar.setTime(processEngineConfiguration.getClock().getCurrentTime());
      gregorianCalendar.add(Calendar.MILLISECOND, getAsyncExecutor().getTimerLockTimeInMillis());
      executableJob.setLockExpirationTime(gregorianCalendar.getTime());
      executableJob.setLockOwner(getAsyncExecutor().getLockOwner());
    }
    
    return executableJob;
  }
  
  protected TimerJobEntity createTimerJobFromOtherJob(AbstractJobEntity otherJob) {
    TimerJobEntity timerJob = processEngineConfiguration.getTimerJobEntityManager().create();
    copyJobInfo(timerJob, otherJob);
    return timerJob;
  }
  
  protected SuspendedJobEntity createSuspendedJobFromOtherJob(AbstractJobEntity otherJob) {
    SuspendedJobEntity suspendedJob = processEngineConfiguration.getSuspendedJobEntityManager().create();
    copyJobInfo(suspendedJob, otherJob);
    return suspendedJob;
  }
  
  protected DeadLetterJobEntity createDeadLetterJobFromOtherJob(AbstractJobEntity otherJob) {
    DeadLetterJobEntity deadLetterJob = processEngineConfiguration.getDeadLetterJobEntityManager().create();
    copyJobInfo(deadLetterJob, otherJob);
    return deadLetterJob;
  }
  
  protected AbstractJobEntity copyJobInfo(AbstractJobEntity copyToJob, AbstractJobEntity copyFromJob) {
    copyToJob.setDuedate(copyFromJob.getDuedate());
    copyToJob.setEndDate(copyFromJob.getEndDate());
    copyToJob.setExclusive(copyFromJob.isExclusive());
    copyToJob.setExecutionId(copyFromJob.getExecutionId());
    copyToJob.setId(copyFromJob.getId());
    copyToJob.setJobHandlerConfiguration(copyFromJob.getJobHandlerConfiguration());
    copyToJob.setJobHandlerType(copyFromJob.getJobHandlerType());
    copyToJob.setJobType(copyFromJob.getJobType());
    copyToJob.setExceptionMessage(copyFromJob.getExceptionMessage());
    copyToJob.setExceptionStacktrace(copyFromJob.getExceptionStacktrace());
    copyToJob.setMaxIterations(copyFromJob.getMaxIterations());
    copyToJob.setProcessDefinitionId(copyFromJob.getProcessDefinitionId());
    copyToJob.setProcessInstanceId(copyFromJob.getProcessInstanceId());
    copyToJob.setRepeat(copyFromJob.getRepeat());
    copyToJob.setRetries(copyFromJob.getRetries());
    copyToJob.setRevision(copyFromJob.getRevision());
    copyToJob.setTenantId(copyFromJob.getTenantId());
    
    return copyToJob;
  }
  
  public ProcessEngineConfigurationImpl getProcessEngineConfiguration() {
    return processEngineConfiguration;
  }

  public void setProcessEngineConfiguration(ProcessEngineConfigurationImpl processEngineConfiguration) {
    this.processEngineConfiguration = processEngineConfiguration;
  }

  protected boolean isAsyncExecutorActive() {
    return processEngineConfiguration.getAsyncExecutor().isActive();
  }
  
  protected CommandContext getCommandContext() {
    return Context.getCommandContext();
  }
  
  protected AsyncExecutor getAsyncExecutor() {
    return processEngineConfiguration.getAsyncExecutor();
  }
  
  protected ExecutionEntityManager getExecutionEntityManager() {
    return processEngineConfiguration.getExecutionEntityManager();
  }
  
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy