protected boolean isFirstJobExecution(JobEntity job) {
   // check if this is jobs' first execution (recognize
   // this because no exception is set. Only the first
   // execution can be without exception - because if
   // no exception occurred the job would have been completed)
   // see https://app.camunda.com/jira/browse/CAM-1039
   return job.getExceptionByteArrayId() == null && job.getExceptionMessage() == null;
 }
  public void testJobExceptionMessageCutoff() {
    JobEntity threeByteJobEntity = new MessageEntity();

    String message = repeatCharacter("a", JobEntity.MAX_EXCEPTION_MESSAGE_LENGTH * 2);
    threeByteJobEntity.setExceptionMessage(message);
    assertEquals(
        JobEntity.MAX_EXCEPTION_MESSAGE_LENGTH, threeByteJobEntity.getExceptionMessage().length());
  }
  public UserOperationLogContextEntryBuilder inContextOf(JobEntity job) {
    entry.setJobDefinitionId(job.getJobDefinitionId());
    entry.setProcessInstanceId(job.getProcessInstanceId());
    entry.setProcessDefinitionId(job.getProcessDefinitionId());
    entry.setProcessDefinitionKey(job.getProcessDefinitionKey());
    entry.setDeploymentId(job.getDeploymentId());

    return this;
  }
  /**
   * Note: This does not test a message with 4-byte Unicode supplementary characters for two
   * reasons: - MySQL 5.1 does not support 4-byte supplementary characters (support from 5.5.3
   * onwards) - {@link String#length()} counts these characters twice (since they are represented by
   * two chars), so essentially the cutoff would be half the actual cutoff for such a string
   */
  public void testInsertJobWithExceptionMessage() {
    String fittingThreeByteMessage =
        repeatCharacter("\u9faf", JobEntity.MAX_EXCEPTION_MESSAGE_LENGTH);

    JobEntity threeByteJobEntity = new MessageEntity();
    threeByteJobEntity.setExceptionMessage(fittingThreeByteMessage);

    // should not fail
    insertJob(threeByteJobEntity);

    deleteJob(threeByteJobEntity);
  }
 protected void initializeRetries(
     JobEntity job, String failedJobRetryTimeCycle, DurationHelper durationHelper) {
   log.fine(
       "Applying JobRetryStrategy '"
           + failedJobRetryTimeCycle
           + "' the first time for job "
           + job.getId()
           + " with "
           + durationHelper.getTimes()
           + " retries");
   job.setRetries(durationHelper.getTimes());
 }
  protected void executeCustomStrategy(
      CommandContext commandContext, JobEntity job, ActivityImpl activity) throws Exception {
    String failedJobRetryTimeCycle = getFailedJobRetryTimeCycle(activity);

    if (failedJobRetryTimeCycle == null) {
      executeStandardStrategy(commandContext);

    } else {
      DurationHelper durationHelper = getDurationHelper(failedJobRetryTimeCycle);

      setLockExpirationTime(job, failedJobRetryTimeCycle, durationHelper);

      if (isFirstJobExecution(job)) {
        // then change default retries to the ones configured
        initializeRetries(job, failedJobRetryTimeCycle, durationHelper);

      } else {
        log.fine(
            "Decrementing retries of JobRetryStrategy '"
                + failedJobRetryTimeCycle
                + "' for job "
                + job.getId());
      }

      logException(job);
      decrementRetries(job);
      notifyAcquisition(commandContext);
    }
  }
  protected void checkAuthorization(CommandContext commandContext) {
    AuthorizationManager authorizationManager = commandContext.getAuthorizationManager();

    if (jobId != null) {

      JobManager jobManager = commandContext.getJobManager();
      JobEntity job = jobManager.findJobById(jobId);

      if (job != null) {

        String processInstanceId = job.getProcessInstanceId();
        if (processInstanceId != null) {
          authorizationManager.checkUpdateProcessInstanceById(processInstanceId);
        } else {
          // start timer job is not assigned to a specific process
          // instance, that's why we have to check whether there
          // exists a UPDATE_INSTANCES permission on process definition or
          // a UPDATE permission on any process instance
          String processDefinitionKey = job.getProcessDefinitionKey();
          if (processDefinitionKey != null) {
            authorizationManager.checkUpdateProcessInstanceByProcessDefinitionKey(
                processDefinitionKey);
          }
        }
        // if (processInstanceId == null && processDefinitionKey == null):
        // job is not assigned to any process instance nor process definition
        // then it is always possible to activate/suspend the corresponding job
        // -> no authorization check necessary
      }
    } else if (jobDefinitionId != null) {

      JobDefinitionManager jobDefinitionManager = commandContext.getJobDefinitionManager();
      JobDefinitionEntity jobDefinition = jobDefinitionManager.findById(jobDefinitionId);

      if (jobDefinition != null) {
        String processDefinitionKey = jobDefinition.getProcessDefinitionKey();
        authorizationManager.checkUpdateProcessInstanceByProcessDefinitionKey(processDefinitionKey);
      }

    } else if (processInstanceId != null) {
      authorizationManager.checkUpdateProcessInstanceById(processInstanceId);
    } else if (processDefinitionId != null) {
      authorizationManager.checkUpdateProcessInstanceByProcessDefinitionId(processDefinitionId);
    } else if (processDefinitionKey != null) {
      authorizationManager.checkUpdateProcessInstanceByProcessDefinitionKey(processDefinitionKey);
    }
  }
  @Deployment(
      resources = {
        "org/camunda/bpm/engine/test/api/mgmt/ManagementServiceTest.testGetJobExceptionStacktrace.bpmn20.xml"
      })
  public void testSetJobRetriesByDefinitionUnlocksInconsistentJobs() {
    // given a job definition
    final JobDefinition jobDefinition = managementService.createJobDefinitionQuery().singleResult();

    // and an inconsistent job that is never again picked up by a job executor
    CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutorTxRequired();
    commandExecutor.execute(
        new Command<Void>() {
          public Void execute(CommandContext commandContext) {
            JobManager jobManager = commandContext.getJobManager();
            MessageEntity job = new MessageEntity();
            job.setJobDefinitionId(jobDefinition.getId());
            job.setJobHandlerType("any");
            job.setLockOwner("owner");
            job.setLockExpirationTime(ClockUtil.getCurrentTime());
            job.setRetries(0);

            jobManager.send(job);
            return null;
          }
        });

    // when the job retries are reset
    managementService.setJobRetriesByJobDefinitionId(jobDefinition.getId(), 3);

    // then the job can be picked up again
    JobEntity job = (JobEntity) managementService.createJobQuery().singleResult();
    assertNotNull(job);
    assertNull(job.getLockOwner());
    assertNull(job.getLockExpirationTime());
    assertEquals(3, job.getRetries());

    deleteJobAndIncidents(job);
  }
  protected ActivityImpl getCurrentActivity(CommandContext commandContext, JobEntity job) {
    String type = job.getJobHandlerType();
    ActivityImpl activity = null;

    String configuration = job.getJobHandlerConfiguration();

    if (TimerExecuteNestedActivityJobHandler.TYPE.equals(type)
        || TimerCatchIntermediateEventJobHandler.TYPE.equals(type)) {
      ExecutionEntity execution = fetchExecutionEntity(job.getExecutionId());
      if (execution != null) {
        String acitivtyId = TimerEventJobHandler.getKey(configuration);
        activity = execution.getProcessDefinition().findActivity(acitivtyId);
      }

    } else if (TimerStartEventJobHandler.TYPE.equals(type)) {
      DeploymentCache deploymentCache =
          Context.getProcessEngineConfiguration().getDeploymentCache();
      String definitionKey = TimerEventJobHandler.getKey(configuration);
      ProcessDefinitionEntity processDefinition =
          deploymentCache.findDeployedLatestProcessDefinitionByKey(definitionKey);
      if (processDefinition != null) {
        activity = processDefinition.getInitial();
      }

    } else if (AsyncContinuationJobHandler.TYPE.equals(type)) {
      ExecutionEntity execution = fetchExecutionEntity(job.getExecutionId());
      if (execution != null) {
        activity = execution.getActivity();
      }

    } else {
      // noop, because activity type is not supported
    }

    return activity;
  }
 @Override
 protected String getValue(JobEntity entity) {
   // JobEntityKey key = new JobEntityKey(entity);
   // return key.toJsonString();
   return entity.getId();
 }
 @Override
 protected String getIndexValue(JobEntity entity) {
   return entity.getExecutionId();
 }
  public void testSetJobRetriesUnlocksInconsistentJob() {
    // case 1
    // given an inconsistent job that is never again picked up by a job executor
    createJob(0, "owner", ClockUtil.getCurrentTime());

    // when the job retries are reset
    JobEntity job = (JobEntity) managementService.createJobQuery().singleResult();
    managementService.setJobRetries(job.getId(), 3);

    // then the job can be picked up again
    job = (JobEntity) managementService.createJobQuery().singleResult();
    assertNotNull(job);
    assertNull(job.getLockOwner());
    assertNull(job.getLockExpirationTime());
    assertEquals(3, job.getRetries());

    deleteJobAndIncidents(job);

    // case 2
    // given an inconsistent job that is never again picked up by a job executor
    createJob(2, "owner", null);

    // when the job retries are reset
    job = (JobEntity) managementService.createJobQuery().singleResult();
    managementService.setJobRetries(job.getId(), 3);

    // then the job can be picked up again
    job = (JobEntity) managementService.createJobQuery().singleResult();
    assertNotNull(job);
    assertNull(job.getLockOwner());
    assertNull(job.getLockExpirationTime());
    assertEquals(3, job.getRetries());

    deleteJobAndIncidents(job);

    // case 3
    // given a consistent job
    createJob(2, "owner", ClockUtil.getCurrentTime());

    // when the job retries are reset
    job = (JobEntity) managementService.createJobQuery().singleResult();
    managementService.setJobRetries(job.getId(), 3);

    // then the lock owner and expiration should not change
    job = (JobEntity) managementService.createJobQuery().singleResult();
    assertNotNull(job);
    assertNotNull(job.getLockOwner());
    assertNotNull(job.getLockExpirationTime());
    assertEquals(3, job.getRetries());

    deleteJobAndIncidents(job);

    TestHelper.clearOpLog(processEngineConfiguration);
  }
 protected void setLockExpirationTime(
     JobEntity job, String failedJobRetryTimeCycle, DurationHelper durationHelper) {
   job.setLockExpirationTime(durationHelper.getDateAfter());
 }