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

com.camunda.consulting.simulator.SimulationExecutor Maven / Gradle / Ivy

Go to download

A Plugin for [Camunda BPM](http://docs.camunda.org) that simulates process execution.

There is a newer version: 1.6.0
Show newest version
package com.camunda.consulting.simulator;

import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.camunda.bpm.engine.ProcessEngine;
import org.camunda.bpm.engine.impl.Page;
import org.camunda.bpm.engine.impl.ProcessDefinitionQueryImpl;
import org.camunda.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.camunda.bpm.engine.impl.interceptor.Command;
import org.camunda.bpm.engine.impl.interceptor.CommandContext;
import org.camunda.bpm.engine.impl.interceptor.CommandExecutor;
import org.camunda.bpm.engine.impl.persistence.entity.JobEntity;
import org.camunda.bpm.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.camunda.bpm.engine.impl.util.ClockUtil;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.camunda.bpm.engine.runtime.Job;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SimulationExecutor {

  private static final Logger LOG = LoggerFactory.getLogger(SimulationExecutor.class);

  // TODO make configurable
  public static final int METRIC_WRITE_INTERVAL_MINUTES = 15;

  private static double progress = 1;

  public static double getProgress() {
    return progress;
  }

  public static void execute(Date start, Date end) {

    ProcessEngineConfigurationImpl processEngineConfigurationImpl = SimulatorPlugin.getProcessEngineConfiguration();
    ProcessEngine processEngine = SimulatorPlugin.getProcessEngine();
    CommandExecutor commandExecutor = processEngineConfigurationImpl.getCommandExecutorTxRequired();

    runWithPreparedEngineConfiguration(processEngineConfigurationImpl, commandExecutor, () -> {

      ClockUtil.setCurrentTime(start);
      progress = 0;
      DateTime lastMetricUpdate = null;

      updateStartTimersForCurrentTime(commandExecutor);

      Optional job;
      do {
        // execute all jobs that are due before current time
        do {
          // work around engine "bug"
          makeTimeGoBy();

          // by setting the processEngineConfigurationImpl.setJobExecutor*
          // properties we can be sure to get the next job with minimum due date
          List jobs = commandExecutor.execute(new Command>() {
            @Override
            public List execute(CommandContext commandContext) {
              return commandContext.getJobManager().findNextJobsToExecute(new Page(0, 1));
            }
          });
          job = jobs.stream().map(jobEntity -> (Job) jobEntity).findFirst();
          job.map(Job::getId).ifPresent(processEngine.getManagementService()::executeJob);

          // write metrics from time to time
          if (lastMetricUpdate == null || lastMetricUpdate.plusMinutes(METRIC_WRITE_INTERVAL_MINUTES).isBefore(ClockUtil.getCurrentTime().getTime())) {
            lastMetricUpdate = new DateTime(ClockUtil.getCurrentTime().getTime());
            processEngineConfigurationImpl.getDbMetricsReporter().reportNow();
          }
        } while (job.isPresent() && (job.get().getDuedate() == null || !job.get().getDuedate().after(end)));

        // get the next job that is due after current time and adjust clock to
        // its due date
        job = processEngine.getManagementService().createJobQuery().orderByJobDuedate().asc().listPage(0, 1).stream().findFirst();
        job.map(Job::getDuedate).ifPresent(ClockUtil::setCurrentTime);
        progress = Math.min(1, (ClockUtil.getCurrentTime().getTime() - start.getTime()) / (double) (end.getTime() - start.getTime()));

        LOG.debug("Advance simulation time to: " + ClockUtil.getCurrentTime());
      } while (job.isPresent() && (job.get().getDuedate() == null || !job.get().getDuedate().after(end)));
    });
  }

  private static void runWithPreparedEngineConfiguration(ProcessEngineConfigurationImpl processEngineConfigurationImpl, CommandExecutor commandExecutor,
      Runnable runnable) {

    boolean metrics = processEngineConfigurationImpl.isMetricsEnabled() && processEngineConfigurationImpl.isDbMetricsReporterActivate();
    boolean jobExecutorEnabled = processEngineConfigurationImpl.getJobExecutor().isActive();

    boolean jobExecutorAcquireByPriority = processEngineConfigurationImpl.isJobExecutorAcquireByPriority();
    boolean jobExecutorPreferTimerJobs = processEngineConfigurationImpl.isJobExecutorPreferTimerJobs();
    boolean jobExecutorAcquireByDueDate = processEngineConfigurationImpl.isJobExecutorAcquireByDueDate();

    if (jobExecutorEnabled) {
      processEngineConfigurationImpl.getJobExecutor().shutdown();
    }

    if (metrics) {
      processEngineConfigurationImpl.getDbMetricsReporter().setReporterId("DEMO-DATA-GENERATOR");
    }

    processEngineConfigurationImpl.setJobExecutorAcquireByPriority(false);
    processEngineConfigurationImpl.setJobExecutorPreferTimerJobs(false);
    processEngineConfigurationImpl.setJobExecutorAcquireByDueDate(true);

    try {
      runnable.run();
    } finally {
      ClockUtil.reset();
      progress = 1;
      
      updateStartTimersForCurrentTime(commandExecutor);

      if (metrics) {
        processEngineConfigurationImpl.getDbMetricsReporter().reportNow();

        processEngineConfigurationImpl.getDbMetricsReporter()
            .setReporterId(processEngineConfigurationImpl.getMetricsReporterIdProvider().provideId(processEngineConfigurationImpl.getProcessEngine()));
      }
      processEngineConfigurationImpl.setJobExecutorAcquireByPriority(jobExecutorAcquireByPriority);
      processEngineConfigurationImpl.setJobExecutorPreferTimerJobs(jobExecutorPreferTimerJobs);
      processEngineConfigurationImpl.setJobExecutorAcquireByDueDate(jobExecutorAcquireByDueDate);
      if (jobExecutorEnabled) {
        processEngineConfigurationImpl.getJobExecutor().start();
      }
    }
  }

  private static void updateStartTimersForCurrentTime(CommandExecutor commandExecutor) {
    commandExecutor.execute(new Command() {
      @Override
      public Void execute(CommandContext commandContext) {
        List list = new ProcessDefinitionQueryImpl(commandExecutor).executeList(commandContext, null);
        list.stream().map(ProcessDefinition::getKey).collect(Collectors.toSet()).forEach(key -> {
          ProcessDefinitionEntity definition = commandContext.getProcessEngineConfiguration().getDeploymentCache()
              .findDeployedLatestProcessDefinitionByKey(key);

          SimulatorPlugin.getBpmnDeployer().adjustStartEventSubscriptions(definition, definition);

        });
        return null;
      }
    });
  }

  private static void makeTimeGoBy() {
    /*
     * Caused by DurationHelper.getDateAfterRepeat: return next.before(date) ?
     * null : next;
     * 
     * This leads to endless loop if we call a timer job at exactly the time it
     * will schedule next. Cannot be handled by engine, because there is no
     * "counter" in the database for executions - it has to trust the clock on
     * the wall.
     * 
     * Hence, we solve that by advancing the time as it would happen in real
     * live systems...
     */
    Calendar cal = Calendar.getInstance();
    cal.setTime(ClockUtil.getCurrentTime());
    cal.add(Calendar.MILLISECOND, 1);
    ClockUtil.setCurrentTime(cal.getTime());
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy