org.flowable.job.service.impl.asyncexecutor.DefaultJobManager.java Source code

Java tutorial

Introduction

Here is the source code for org.flowable.job.service.impl.asyncexecutor.DefaultJobManager.java

Source

/* 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.flowable.job.service.impl.asyncexecutor;

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

import org.apache.commons.lang3.StringUtils;
import org.flowable.engine.common.api.FlowableException;
import org.flowable.engine.common.api.FlowableIllegalArgumentException;
import org.flowable.engine.common.api.delegate.event.FlowableEngineEventType;
import org.flowable.engine.common.api.delegate.event.FlowableEventDispatcher;
import org.flowable.engine.common.impl.calendar.BusinessCalendar;
import org.flowable.engine.common.impl.cfg.TransactionState;
import org.flowable.engine.common.impl.context.Context;
import org.flowable.engine.common.impl.interceptor.CommandContext;
import org.flowable.job.api.HistoryJob;
import org.flowable.job.api.Job;
import org.flowable.job.api.JobInfo;
import org.flowable.job.service.HistoryJobHandler;
import org.flowable.job.service.HistoryJobProcessor;
import org.flowable.job.service.HistoryJobProcessorContext;
import org.flowable.job.service.JobHandler;
import org.flowable.job.service.JobProcessor;
import org.flowable.job.service.JobProcessorContext;
import org.flowable.job.service.JobServiceConfiguration;
import org.flowable.job.service.event.impl.FlowableJobEventBuilder;
import org.flowable.job.service.impl.HistoryJobProcessorContextImpl;
import org.flowable.job.service.impl.JobProcessorContextImpl;
import org.flowable.job.service.impl.persistence.entity.AbstractJobEntity;
import org.flowable.job.service.impl.persistence.entity.AbstractRuntimeJobEntity;
import org.flowable.job.service.impl.persistence.entity.DeadLetterJobEntity;
import org.flowable.job.service.impl.persistence.entity.HistoryJobEntity;
import org.flowable.job.service.impl.persistence.entity.JobByteArrayRef;
import org.flowable.job.service.impl.persistence.entity.JobEntity;
import org.flowable.job.service.impl.persistence.entity.SuspendedJobEntity;
import org.flowable.job.service.impl.persistence.entity.TimerJobEntity;
import org.flowable.job.service.impl.persistence.entity.TimerJobEntityManager;
import org.flowable.job.service.impl.util.CommandContextUtil;
import org.flowable.variable.api.delegate.VariableScope;
import org.flowable.variable.service.impl.el.NoExecutionVariableScope;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.databind.JsonNode;

public class DefaultJobManager implements JobManager {

    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultJobManager.class);

    public static final String CYCLE_TYPE = "cycle";

    protected JobServiceConfiguration jobServiceConfiguration;

    public DefaultJobManager() {
    }

    public DefaultJobManager(JobServiceConfiguration jobServiceConfiguration) {
        this.jobServiceConfiguration = jobServiceConfiguration;
    }

    @Override
    public void setAsyncJobProperties(JobEntity jobEntity, boolean exclusive) {
        // When the async executor is activated, the job is directly passed on to the async executor thread
        if (isAsyncExecutorActive()) {
            internalCreateLockedAsyncJob(jobEntity, exclusive);

        } else {
            internalCreateAsyncJob(jobEntity, exclusive);
        }
    }

    @Override
    public void scheduleAsyncJob(JobEntity jobEntity) {
        callJobProcessors(JobProcessorContext.Phase.BEFORE_CREATE, jobEntity);
        jobServiceConfiguration.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 void scheduleTimerJob(TimerJobEntity timerJob) {
        scheduleTimer(timerJob);
        sendTimerScheduledEvent(timerJob);
    }

    private void scheduleTimer(TimerJobEntity timerJob) {
        if (timerJob == null) {
            throw new FlowableException("Empty timer job can not be scheduled");
        }
        callJobProcessors(JobProcessorContext.Phase.BEFORE_CREATE, timerJob);
        jobServiceConfiguration.getTimerJobEntityManager().insert(timerJob);
    }

    private void sendTimerScheduledEvent(TimerJobEntity timerJob) {
        FlowableEventDispatcher eventDispatcher = CommandContextUtil.getEventDispatcher();
        if (eventDispatcher != null && eventDispatcher.isEnabled()) {
            eventDispatcher.dispatchEvent(
                    FlowableJobEventBuilder.createEntityEvent(FlowableEngineEventType.TIMER_SCHEDULED, timerJob));
        }
    }

    @Override
    public JobEntity moveTimerJobToExecutableJob(TimerJobEntity timerJob) {
        if (timerJob == null) {
            throw new FlowableException("Empty timer job can not be scheduled");
        }

        JobEntity executableJob = createExecutableJobFromOtherJob(timerJob);
        boolean insertSuccessful = jobServiceConfiguration.getJobEntityManager().insertJobEntity(executableJob);
        if (insertSuccessful) {
            jobServiceConfiguration.getTimerJobEntityManager().delete(timerJob);
            triggerExecutorIfNeeded(executableJob);
            return executableJob;
        }
        return null;
    }

    @Override
    public TimerJobEntity moveJobToTimerJob(AbstractRuntimeJobEntity job) {
        TimerJobEntity timerJob = createTimerJobFromOtherJob(job);
        boolean insertSuccessful = jobServiceConfiguration.getTimerJobEntityManager()
                .insertTimerJobEntity(timerJob);
        if (insertSuccessful) {
            if (job instanceof JobEntity) {
                jobServiceConfiguration.getJobEntityManager().delete((JobEntity) job);
            } else if (job instanceof SuspendedJobEntity) {
                jobServiceConfiguration.getSuspendedJobEntityManager().delete((SuspendedJobEntity) job);
            }

            return timerJob;
        }
        return null;
    }

    @Override
    public SuspendedJobEntity moveJobToSuspendedJob(AbstractRuntimeJobEntity job) {
        SuspendedJobEntity suspendedJob = createSuspendedJobFromOtherJob(job);
        jobServiceConfiguration.getSuspendedJobEntityManager().insert(suspendedJob);
        if (job instanceof TimerJobEntity) {
            jobServiceConfiguration.getTimerJobEntityManager().delete((TimerJobEntity) job);

        } else if (job instanceof JobEntity) {
            jobServiceConfiguration.getJobEntityManager().delete((JobEntity) job);
        }

        return suspendedJob;
    }

    @Override
    public AbstractRuntimeJobEntity activateSuspendedJob(SuspendedJobEntity job) {
        AbstractRuntimeJobEntity activatedJob = null;
        if (Job.JOB_TYPE_TIMER.equals(job.getJobType())) {
            activatedJob = createTimerJobFromOtherJob(job);
            jobServiceConfiguration.getTimerJobEntityManager().insert((TimerJobEntity) activatedJob);

        } else {
            activatedJob = createExecutableJobFromOtherJob(job);
            JobEntity jobEntity = (JobEntity) activatedJob;
            jobServiceConfiguration.getJobEntityManager().insert(jobEntity);
            triggerExecutorIfNeeded(jobEntity);
        }

        jobServiceConfiguration.getSuspendedJobEntityManager().delete(job);
        return activatedJob;
    }

    @Override
    public DeadLetterJobEntity moveJobToDeadLetterJob(AbstractRuntimeJobEntity job) {
        DeadLetterJobEntity deadLetterJob = createDeadLetterJobFromOtherJob(job);
        jobServiceConfiguration.getDeadLetterJobEntityManager().insert(deadLetterJob);
        if (job instanceof TimerJobEntity) {
            jobServiceConfiguration.getTimerJobEntityManager().delete((TimerJobEntity) job);

        } else if (job instanceof JobEntity) {
            jobServiceConfiguration.getJobEntityManager().delete((JobEntity) job);
        }

        return deadLetterJob;
    }

    @Override
    public JobEntity moveDeadLetterJobToExecutableJob(DeadLetterJobEntity deadLetterJobEntity, int retries) {
        if (deadLetterJobEntity == null) {
            throw new FlowableIllegalArgumentException("Null job provided");
        }

        JobEntity executableJob = createExecutableJobFromOtherJob(deadLetterJobEntity);
        executableJob.setRetries(retries);
        boolean insertSuccessful = jobServiceConfiguration.getJobEntityManager().insertJobEntity(executableJob);
        if (insertSuccessful) {
            jobServiceConfiguration.getDeadLetterJobEntityManager().delete(deadLetterJobEntity);
            triggerExecutorIfNeeded(executableJob);
            return executableJob;
        }
        return null;
    }

    @Override
    public void execute(JobInfo job) {
        if (job instanceof HistoryJobEntity) {
            callHistoryJobProcessors(HistoryJobProcessorContext.Phase.BEFORE_EXECUTE, (HistoryJobEntity) job);
            executeHistoryJob((HistoryJobEntity) job);
        } else if (job instanceof JobEntity) {
            callJobProcessors(JobProcessorContext.Phase.BEFORE_EXECUTE, (JobEntity) job);
            if (Job.JOB_TYPE_MESSAGE.equals(((Job) job).getJobType())) {
                executeMessageJob((JobEntity) job);
            } else if (Job.JOB_TYPE_TIMER.equals(((Job) job).getJobType())) {
                executeTimerJob((JobEntity) job);
            }

        } else {
            throw new FlowableException("Only jobs with type JobEntity are supported to be executed");
        }
    }

    @Override
    public void unacquire(JobInfo job) {

        if (job instanceof HistoryJob) {

            HistoryJobEntity jobEntity = (HistoryJobEntity) job;

            HistoryJobEntity newJobEntity = jobServiceConfiguration.getHistoryJobEntityManager().create();
            copyHistoryJobInfo(newJobEntity, jobEntity);
            newJobEntity.setId(null); // We want a new id to be assigned to this job
            newJobEntity.setLockExpirationTime(null);
            newJobEntity.setLockOwner(null);
            jobServiceConfiguration.getHistoryJobEntityManager().insert(newJobEntity);
            jobServiceConfiguration.getHistoryJobEntityManager().deleteNoCascade(jobEntity);

        } else if (job instanceof JobEntity) {

            // 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)

            JobEntity jobEntity = (JobEntity) job;

            JobEntity newJobEntity = jobServiceConfiguration.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);
            jobServiceConfiguration.getJobEntityManager().insert(newJobEntity);
            jobServiceConfiguration.getJobEntityManager().delete(jobEntity.getId());

            // We're not calling triggerExecutorIfNeeded here after the insert. 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.
            jobServiceConfiguration.getJobEntityManager().resetExpiredJob(job.getId());
        }

    }

    @Override
    public void unacquireWithDecrementRetries(JobInfo job) {
        if (job instanceof HistoryJob) {
            HistoryJobEntity historyJobEntity = (HistoryJobEntity) job;

            if (historyJobEntity.getRetries() > 0) {
                HistoryJobEntity newHistoryJobEntity = jobServiceConfiguration.getHistoryJobEntityManager()
                        .create();
                copyHistoryJobInfo(newHistoryJobEntity, historyJobEntity);
                newHistoryJobEntity.setId(null); // We want a new id to be assigned to this job
                newHistoryJobEntity.setLockExpirationTime(null);
                newHistoryJobEntity.setLockOwner(null);
                newHistoryJobEntity.setCreateTime(jobServiceConfiguration.getClock().getCurrentTime());

                newHistoryJobEntity.setRetries(newHistoryJobEntity.getRetries() - 1);
                jobServiceConfiguration.getHistoryJobEntityManager().insert(newHistoryJobEntity);
                jobServiceConfiguration.getHistoryJobEntityManager().deleteNoCascade(historyJobEntity);

            } else {
                jobServiceConfiguration.getHistoryJobEntityManager().delete(historyJobEntity);
            }

        } else {
            JobEntity jobEntity = (JobEntity) job;

            JobEntity newJobEntity = jobServiceConfiguration.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);

            if (newJobEntity.getRetries() > 0) {
                newJobEntity.setRetries(newJobEntity.getRetries() - 1);
                jobServiceConfiguration.getJobEntityManager().insert(newJobEntity);

            } else {
                DeadLetterJobEntity deadLetterJob = createDeadLetterJobFromOtherJob(newJobEntity);
                jobServiceConfiguration.getDeadLetterJobEntityManager().insert(deadLetterJob);
            }

            jobServiceConfiguration.getJobEntityManager().delete(jobEntity.getId());

            // We're not calling triggerExecutorIfNeeded here after the insert. 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.

        }
    }

    protected void executeMessageJob(JobEntity jobEntity) {
        executeJobHandler(jobEntity);
        if (jobEntity.getId() != null) {
            CommandContextUtil.getJobEntityManager().delete(jobEntity);
        }
    }

    protected void executeHistoryJob(HistoryJobEntity historyJobEntity) {
        executeHistoryJobHandler(historyJobEntity);
        if (historyJobEntity.getId() != null) {
            CommandContextUtil.getHistoryJobEntityManager().delete(historyJobEntity);
        }
    }

    protected void executeTimerJob(JobEntity timerEntity) {
        TimerJobEntityManager timerJobEntityManager = jobServiceConfiguration.getTimerJobEntityManager();

        VariableScope variableScope = jobServiceConfiguration.getInternalJobManager()
                .resolveVariableScope(timerEntity);

        if (variableScope == null) {
            variableScope = NoExecutionVariableScope.getSharedInstance();
        }

        jobServiceConfiguration.getInternalJobManager().preTimerJobDelete(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());
            }
            jobServiceConfiguration.getJobEntityManager().delete(timerEntity);
            return;
        }

        executeJobHandler(timerEntity);
        jobServiceConfiguration.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) {
                jobServiceConfiguration.getInternalJobManager().preRepeatedTimerSchedule(newTimerJobEntity,
                        variableScope);
                scheduleTimerJob(newTimerJobEntity);
            }
        }
    }

    protected void executeJobHandler(JobEntity jobEntity) {
        VariableScope variableScope = jobServiceConfiguration.getInternalJobManager()
                .resolveVariableScope(jobEntity);

        Map<String, JobHandler> jobHandlers = jobServiceConfiguration.getJobHandlers();
        JobHandler jobHandler = jobHandlers.get(jobEntity.getJobHandlerType());
        jobHandler.execute(jobEntity, jobEntity.getJobHandlerConfiguration(), variableScope, getCommandContext());
    }

    protected void executeHistoryJobHandler(HistoryJobEntity historyJobEntity) {
        Map<String, HistoryJobHandler> jobHandlers = jobServiceConfiguration.getHistoryJobHandlers();
        HistoryJobHandler jobHandler = jobHandlers.get(historyJobEntity.getJobHandlerType());
        jobHandler.execute(historyJobEntity, historyJobEntity.getJobHandlerConfiguration(), getCommandContext());
    }

    protected boolean isValidTime(JobEntity timerEntity, Date newTimerDate, VariableScope variableScope) {
        BusinessCalendar businessCalendar = jobServiceConfiguration.getBusinessCalendarManager()
                .getBusinessCalendar(getBusinessCalendarName(timerEntity, variableScope));
        return businessCalendar.validateDuedate(timerEntity.getRepeat(), timerEntity.getMaxIterations(),
                timerEntity.getEndDate(), newTimerDate);
    }

    protected void hintAsyncExecutor(JobEntity job) {
        if (Context.getTransactionContext() != null) {
            JobAddedTransactionListener jobAddedTransactionListener = new JobAddedTransactionListener(job,
                    getAsyncExecutor());
            Context.getTransactionContext().addTransactionListener(TransactionState.COMMITTED,
                    jobAddedTransactionListener);
        } else {
            AsyncJobAddedNotification jobAddedNotification = new AsyncJobAddedNotification(job, getAsyncExecutor());
            getCommandContext().addCloseListener(jobAddedNotification);
        }
    }

    @Override
    public String getBusinessCalendarName(JobEntity timerEntity, VariableScope variableScope) {
        String calendarValue = null;
        if (StringUtils.isNotEmpty(timerEntity.getJobHandlerConfiguration())) {
            try {
                JsonNode jobConfigNode = jobServiceConfiguration.getObjectMapper()
                        .readTree(timerEntity.getJobHandlerConfiguration());
                JsonNode calendarNameNode = jobConfigNode.get("calendarName");
                if (calendarNameNode != null && !calendarNameNode.isNull()) {
                    calendarValue = calendarNameNode.asText();
                }

            } catch (Exception e) {
                // ignore JSON exception
            }
        }

        return getBusinessCalendarName(calendarValue, variableScope);
    }

    protected String getBusinessCalendarName(String calendarName, VariableScope variableScope) {
        String businessCalendarName = CYCLE_TYPE;
        if (StringUtils.isNotEmpty(calendarName)) {
            businessCalendarName = (String) CommandContextUtil.getJobServiceConfiguration().getExpressionManager()
                    .createExpression(calendarName).getValue(variableScope);
        }
        return businessCalendarName;
    }

    @Override
    public HistoryJobEntity scheduleHistoryJob(HistoryJobEntity historyJobEntity) {
        callHistoryJobProcessors(HistoryJobProcessorContext.Phase.BEFORE_CREATE, historyJobEntity);
        jobServiceConfiguration.getHistoryJobEntityManager().insert(historyJobEntity);
        return historyJobEntity;
    }

    protected void internalCreateAsyncJob(JobEntity jobEntity, boolean exclusive) {
        fillDefaultAsyncJobInfo(jobEntity, exclusive);
    }

    protected void internalCreateLockedAsyncJob(JobEntity jobEntity, boolean exclusive) {
        fillDefaultAsyncJobInfo(jobEntity, exclusive);

        GregorianCalendar gregorianCalendar = new GregorianCalendar();
        gregorianCalendar.setTime(jobServiceConfiguration.getClock().getCurrentTime());
        gregorianCalendar.add(Calendar.MILLISECOND, getAsyncExecutor().getAsyncJobLockTimeInMillis());
        jobEntity.setLockExpirationTime(gregorianCalendar.getTime());
        jobEntity.setLockOwner(getAsyncExecutor().getLockOwner());
    }

    protected void fillDefaultAsyncJobInfo(JobEntity jobEntity, boolean exclusive) {
        jobEntity.setJobType(JobEntity.JOB_TYPE_MESSAGE);
        jobEntity.setRevision(1);
        jobEntity.setRetries(jobServiceConfiguration.getAsyncExecutorNumberOfRetries());
        jobEntity.setExclusive(exclusive);
    }

    protected JobEntity createExecutableJobFromOtherJob(AbstractRuntimeJobEntity job) {
        JobEntity executableJob = jobServiceConfiguration.getJobEntityManager().create();
        copyJobInfo(executableJob, job);

        if (isAsyncExecutorActive()) {
            GregorianCalendar gregorianCalendar = new GregorianCalendar();
            gregorianCalendar.setTime(jobServiceConfiguration.getClock().getCurrentTime());
            gregorianCalendar.add(Calendar.MILLISECOND, getAsyncExecutor().getTimerLockTimeInMillis());
            executableJob.setLockExpirationTime(gregorianCalendar.getTime());
            executableJob.setLockOwner(getAsyncExecutor().getLockOwner());
        }

        return executableJob;
    }

    protected TimerJobEntity createTimerJobFromOtherJob(AbstractRuntimeJobEntity otherJob) {
        TimerJobEntity timerJob = jobServiceConfiguration.getTimerJobEntityManager().create();
        copyJobInfo(timerJob, otherJob);
        return timerJob;
    }

    protected SuspendedJobEntity createSuspendedJobFromOtherJob(AbstractRuntimeJobEntity otherJob) {
        SuspendedJobEntity suspendedJob = jobServiceConfiguration.getSuspendedJobEntityManager().create();
        copyJobInfo(suspendedJob, otherJob);
        return suspendedJob;
    }

    protected DeadLetterJobEntity createDeadLetterJobFromOtherJob(AbstractRuntimeJobEntity otherJob) {
        DeadLetterJobEntity deadLetterJob = jobServiceConfiguration.getDeadLetterJobEntityManager().create();
        copyJobInfo(deadLetterJob, otherJob);
        return deadLetterJob;
    }

    protected AbstractRuntimeJobEntity copyJobInfo(AbstractRuntimeJobEntity copyToJob,
            AbstractRuntimeJobEntity 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.setCustomValues(copyFromJob.getCustomValues());
        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.setScopeId(copyFromJob.getScopeId());
        copyToJob.setSubScopeId(copyFromJob.getSubScopeId());
        copyToJob.setScopeType(copyFromJob.getScopeType());
        copyToJob.setScopeDefinitionId(copyFromJob.getScopeDefinitionId());
        copyToJob.setRepeat(copyFromJob.getRepeat());
        copyToJob.setRetries(copyFromJob.getRetries());
        copyToJob.setRevision(copyFromJob.getRevision());
        copyToJob.setTenantId(copyFromJob.getTenantId());

        return copyToJob;
    }

    protected HistoryJobEntity copyHistoryJobInfo(HistoryJobEntity copyToJob, HistoryJobEntity copyFromJob) {
        copyToJob.setId(copyFromJob.getId());
        copyToJob.setJobHandlerConfiguration(copyFromJob.getJobHandlerConfiguration());
        if (copyFromJob.getAdvancedJobHandlerConfigurationByteArrayRef() != null) {
            JobByteArrayRef configurationByteArrayRefCopy = copyFromJob
                    .getAdvancedJobHandlerConfigurationByteArrayRef().copy();
            copyToJob.setAdvancedJobHandlerConfigurationByteArrayRef(configurationByteArrayRefCopy);
        }
        if (copyFromJob.getExceptionByteArrayRef() != null) {
            JobByteArrayRef exceptionByteArrayRefCopy = copyFromJob.getExceptionByteArrayRef();
            copyToJob.setExceptionByteArrayRef(exceptionByteArrayRefCopy);
        }
        if (copyFromJob.getCustomValuesByteArrayRef() != null) {
            JobByteArrayRef customValuesByteArrayRefCopy = copyFromJob.getCustomValuesByteArrayRef().copy();
            copyToJob.setCustomValuesByteArrayRef(customValuesByteArrayRefCopy);
        }
        copyToJob.setJobHandlerType(copyFromJob.getJobHandlerType());
        copyToJob.setExceptionMessage(copyFromJob.getExceptionMessage());
        copyToJob.setExceptionStacktrace(copyFromJob.getExceptionStacktrace());
        copyToJob.setCustomValues(copyFromJob.getCustomValues());
        copyToJob.setRetries(copyFromJob.getRetries());
        copyToJob.setRevision(copyFromJob.getRevision());
        copyToJob.setTenantId(copyFromJob.getTenantId());

        return copyToJob;
    }

    public JobServiceConfiguration getJobServiceConfiguration() {
        return jobServiceConfiguration;
    }

    @Override
    public void setJobServiceConfiguration(JobServiceConfiguration jobServiceConfiguration) {
        this.jobServiceConfiguration = jobServiceConfiguration;
    }

    protected boolean isAsyncExecutorActive() {
        return jobServiceConfiguration.getAsyncExecutor() != null
                && jobServiceConfiguration.getAsyncExecutor().isActive();
    }

    protected CommandContext getCommandContext() {
        return Context.getCommandContext();
    }

    protected AsyncExecutor getAsyncExecutor() {
        return jobServiceConfiguration.getAsyncExecutor();
    }

    protected void callJobProcessors(JobProcessorContext.Phase processorType, AbstractJobEntity abstractJobEntity) {
        if (jobServiceConfiguration.getJobProcessors() != null) {
            JobProcessorContextImpl jobProcessorContext = new JobProcessorContextImpl(processorType,
                    abstractJobEntity);
            for (JobProcessor jobProcessor : jobServiceConfiguration.getJobProcessors()) {
                jobProcessor.process(jobProcessorContext);
            }
        }
    }

    protected void callHistoryJobProcessors(HistoryJobProcessorContext.Phase processorType,
            HistoryJobEntity historyJobEntity) {
        if (jobServiceConfiguration.getHistoryJobProcessors() != null) {
            HistoryJobProcessorContextImpl historyJobProcessorContext = new HistoryJobProcessorContextImpl(
                    processorType, historyJobEntity);
            for (HistoryJobProcessor historyJobProcessor : jobServiceConfiguration.getHistoryJobProcessors()) {
                historyJobProcessor.process(historyJobProcessorContext);
            }
        }
    }

}