content: /*
   * Copyright 2010-2025 Hyland Software, Inc. and its affiliates.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.activiti.engine.test.api.mgmt;
  
  import static org.assertj.core.api.Assertions.assertThat;
  import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
  
  import java.util.Date;
  import org.activiti.engine.ActivitiException;
  import org.activiti.engine.ActivitiIllegalArgumentException;
  import org.activiti.engine.ActivitiObjectNotFoundException;
  import org.activiti.engine.JobNotFoundException;
  import org.activiti.engine.impl.ProcessEngineImpl;
  import org.activiti.engine.impl.cmd.AcquireTimerJobsCmd;
  import org.activiti.engine.impl.interceptor.CommandExecutor;
  import org.activiti.engine.impl.persistence.entity.EventSubscriptionEntity;
  import org.activiti.engine.impl.test.PluggableActivitiTestCase;
  import org.activiti.engine.management.TableMetaData;
  import org.activiti.engine.runtime.Job;
  import org.activiti.engine.runtime.ProcessInstance;
  import org.activiti.engine.test.Deployment;
  
  /**
   */
  public class ManagementServiceTest extends PluggableActivitiTestCase {
  
      public void testGetMetaDataForUnexistingTable() {
          TableMetaData metaData = managementService.getTableMetaData("unexistingtable");
          assertThat(metaData).isNull();
      }
  
      public void testGetMetaDataNullTableName() {
          assertThatExceptionOfType(ActivitiIllegalArgumentException.class)
              .isThrownBy(() -> managementService.getTableMetaData(null))
              .withMessageContaining("tableName is null");
      }
  
      public void testExecuteJobNullJobId() {
          assertThatExceptionOfType(ActivitiIllegalArgumentException.class)
              .isThrownBy(() -> managementService.executeJob(null))
              .withMessageContaining("JobId is null");
      }
  
      public void testExecuteJobUnexistingJob() {
          assertThatExceptionOfType(JobNotFoundException.class)
              .isThrownBy(() -> managementService.executeJob("unexistingjob"))
              .withMessageContaining("No job found with id")
              .satisfies(ae -> assertThat(ae.getObjectClass()).isEqualTo(Job.class));
      }
  
      @Deployment
      public void testGetJobExceptionStacktrace() {
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("exceptionInJobExecution");
  
          // The execution is waiting in the first usertask. This contains a boundary
          // timer event which we will execute manual for testing purposes.
          Job timerJob = managementService
              .createTimerJobQuery()
              .processInstanceId(processInstance.getId())
              .singleResult();
  
          assertThat(timerJob).as("No job found for process instance").isNotNull();
  
          assertThatExceptionOfType(RuntimeException.class)
              .as("RuntimeException from within the script task expected")
              .isThrownBy(() -> {
                  managementService.moveTimerToExecutableJob(timerJob.getId());
                  managementService.executeJob(timerJob.getId());
              })
              .withMessageContaining("This is an exception thrown from scriptTask");
  
          // Fetch the task to see that the exception that occurred is persisted
          Job reloadedTimerJob = managementService
              .createTimerJobQuery()
              .processInstanceId(processInstance.getId())
              .singleResult();
  
          assertThat(reloadedTimerJob).isNotNull();
          assertThat(reloadedTimerJob.getExceptionMessage()).isNotNull();
          assertThat(reloadedTimerJob.getExceptionMessage()).contains("This is an exception thrown from scriptTask");
  
          // Get the full stacktrace using the managementService
          String exceptionStack = managementService.getTimerJobExceptionStacktrace(reloadedTimerJob.getId());
          assertThat(exceptionStack).isNotNull();
          assertThat(exceptionStack).contains("This is an exception thrown from scriptTask");
      }
  
      public void testGetJobExceptionStacktraceUnexistingJobId() {
          assertThatExceptionOfType(ActivitiObjectNotFoundException.class)
              .isThrownBy(() -> managementService.getJobExceptionStacktrace("unexistingjob"))
              .withMessageContaining("No job found with id unexistingjob")
              .satisfies(ae -> assertThat(ae.getObjectClass()).isEqualTo(Job.class));
      }
  
      public void testgetJobExceptionStacktraceNullJobId() {
          assertThatExceptionOfType(ActivitiIllegalArgumentException.class)
              .isThrownBy(() -> managementService.getJobExceptionStacktrace(null))
              .withMessageContaining("jobId is null");
      }
  
      @Deployment(
          resources = {
              "org/activiti/engine/test/api/mgmt/ManagementServiceTest.testGetJobExceptionStacktrace.bpmn20.xml",
          }
      )
      public void testSetJobRetries() {
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("exceptionInJobExecution");
  
          // The execution is waiting in the first usertask. This contains a boundary timer event.
          Job timerJob = managementService
              .createTimerJobQuery()
              .processInstanceId(processInstance.getId())
              .singleResult();
  
          Date duedate = timerJob.getDuedate();
  
          assertThat(timerJob).as("No job found for process instance").isNotNull();
          assertThat(timerJob.getRetries()).isEqualTo(processEngineConfiguration.getAsyncExecutorNumberOfRetries());
  
          managementService.setTimerJobRetries(timerJob.getId(), 5);
  
          timerJob = managementService.createTimerJobQuery().processInstanceId(processInstance.getId()).singleResult();
          assertThat(timerJob.getRetries()).isEqualTo(5);
          assertThat(timerJob.getDuedate()).isEqualTo(duedate);
      }
  
      public void testSetJobRetriesUnexistingJobId() {
          assertThatExceptionOfType(ActivitiObjectNotFoundException.class)
              .isThrownBy(() -> managementService.setJobRetries("unexistingjob", 5))
              .withMessageContaining("No job found with id 'unexistingjob'.")
              .satisfies(ae -> assertThat(ae.getObjectClass()).isEqualTo(Job.class));
      }
  
      public void testSetJobRetriesEmptyJobId() {
          assertThatExceptionOfType(ActivitiIllegalArgumentException.class)
              .isThrownBy(() -> managementService.setJobRetries("", 5))
              .withMessageContaining("The job id is mandatory, but '' has been provided.");
      }
  
      public void testSetJobRetriesJobIdNull() {
          assertThatExceptionOfType(ActivitiIllegalArgumentException.class)
              .isThrownBy(() -> managementService.setJobRetries(null, 5))
              .withMessageContaining("The job id is mandatory, but 'null' has been provided.");
      }
  
      public void testSetJobRetriesNegativeNumberOfRetries() {
          assertThatExceptionOfType(ActivitiIllegalArgumentException.class)
              .isThrownBy(() -> managementService.setJobRetries("unexistingjob", -1))
              .withMessageContaining(
                  "The number of job retries must be a non-negative Integer, but '-1' has been provided."
              );
      }
  
      public void testDeleteJobNullJobId() {
          assertThatExceptionOfType(ActivitiIllegalArgumentException.class)
              .isThrownBy(() -> managementService.deleteJob(null))
              .withMessageContaining("jobId is null");
      }
  
      public void testDeleteJobUnexistingJob() {
          assertThatExceptionOfType(ActivitiObjectNotFoundException.class)
              .isThrownBy(() -> managementService.deleteJob("unexistingjob"))
              .withMessageContaining("No job found with id")
              .satisfies(ae -> assertThat(ae.getObjectClass()).isEqualTo(Job.class));
      }
  
      @Deployment(resources = { "org/activiti/engine/test/api/mgmt/timerOnTask.bpmn20.xml" })
      public void testDeleteJobDeletion() {
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("timerOnTask");
          Job timerJob = managementService
              .createTimerJobQuery()
              .processInstanceId(processInstance.getId())
              .singleResult();
  
          assertThat(timerJob).as("Task timer should be there").isNotNull();
          managementService.deleteTimerJob(timerJob.getId());
  
          timerJob = managementService.createTimerJobQuery().processInstanceId(processInstance.getId()).singleResult();
          assertThat(timerJob).as("There should be no job now. It was deleted").isNull();
      }
  
      @Deployment(resources = { "org/activiti/engine/test/api/mgmt/timerOnTask.bpmn20.xml" })
      public void testDeleteJobThatWasAlreadyAcquired() {
          processEngineConfiguration.getClock().setCurrentTime(new Date());
  
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("timerOnTask");
          Job timerJob = managementService
              .createTimerJobQuery()
              .processInstanceId(processInstance.getId())
              .singleResult();
  
          // We need to move time at least one hour to make the timer executable
          processEngineConfiguration
              .getClock()
              .setCurrentTime(new Date(processEngineConfiguration.getClock().getCurrentTime().getTime() + 7200000L));
  
          // Acquire job by running the acquire command manually
          ProcessEngineImpl processEngineImpl = (ProcessEngineImpl) processEngine;
          AcquireTimerJobsCmd acquireJobsCmd = new AcquireTimerJobsCmd(
              processEngine.getProcessEngineConfiguration().getAsyncExecutor()
          );
          CommandExecutor commandExecutor = processEngineImpl.getProcessEngineConfiguration().getCommandExecutor();
          commandExecutor.execute(acquireJobsCmd);
  
          // Try to delete the job. This should fail.
          assertThatExceptionOfType(ActivitiException.class).isThrownBy(() ->
              managementService.deleteJob(timerJob.getId())
          );
  
          // Clean up
          managementService.moveTimerToExecutableJob(timerJob.getId());
          managementService.executeJob(timerJob.getId());
      }
  
      // https://jira.codehaus.org/browse/ACT-1816:
      // ManagementService doesn't seem to give actual table Name for EventSubscriptionEntity.class
      public void testGetTableName() {
          String table = managementService.getTableName(EventSubscriptionEntity.class);
          assertThat(table).isEqualTo("ACT_RU_EVENT_SUBSCR");
      }
  }
  