/* 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.impl.cmd;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.ProcessEngineConfiguration;
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.DurationHelper;
import org.activiti.engine.impl.cfg.TransactionContext;
import org.activiti.engine.impl.cfg.TransactionState;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.jobexecutor.AsyncContinuationJobHandler;
import org.activiti.engine.impl.jobexecutor.JobAddedNotification;
import org.activiti.engine.impl.jobexecutor.JobExecutor;
import org.activiti.engine.impl.jobexecutor.TimerCatchIntermediateEventJobHandler;
import org.activiti.engine.impl.jobexecutor.TimerExecuteNestedActivityJobHandler;
import org.activiti.engine.impl.jobexecutor.TimerStartEventJobHandler;
import org.activiti.engine.impl.persistence.deploy.DeploymentManager;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.JobEntity;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Saeid Mirzaei
*/
public class JobRetryCmd implements Command<Object> {
private static final Logger log = LoggerFactory.getLogger(JobRetryCmd.class.getName());
protected String jobId;
protected Throwable exception;
public JobRetryCmd(String jobId, Throwable exception) {
this.jobId = jobId;
this.exception = exception;
}
public Object execute(CommandContext commandContext) {
JobEntity job = commandContext.getJobEntityManager().findJobById(jobId);
if (job == null) {
return null;
}
ActivityImpl activity = getCurrentActivity(commandContext, job);
ProcessEngineConfiguration processEngineConfig = commandContext.getProcessEngineConfiguration();
if (activity == null || activity.getFailedJobRetryTimeCycleValue() == null) {
log.error("activitiy or FailedJobRetryTimerCycleValue is null in job " + jobId + "'. only decrementing retries.");
job.setRetries(job.getRetries() - 1);
job.setLockOwner(null);
job.setLockExpirationTime(null);
if (job.getDuedate() == null) {
// add wait time for failed async job
job.setDuedate(calculateDueDate(commandContext, processEngineConfig.getAsyncFailedJobWaitTime(), null));
} else {
// add default wait time for failed job
job.setDuedate(calculateDueDate(commandContext, processEngineConfig.getDefaultFailedJobWaitTime(), job.getDuedate()));
}
} else {
String failedJobRetryTimeCycle = activity.getFailedJobRetryTimeCycleValue();
try {
DurationHelper durationHelper = new DurationHelper(failedJobRetryTimeCycle, processEngineConfig.getClock());
job.setLockOwner(null);
job.setLockExpirationTime(null);
job.setDuedate(durationHelper.getDateAfter());
if (job.getExceptionMessage() == null) { // is it the first exception
log.debug("Applying JobRetryStrategy '" + failedJobRetryTimeCycle+ "' the first time for job " + job.getId() + " with "+ durationHelper.getTimes()+" retries");
// then change default retries to the ones configured
job.setRetries(durationHelper.getTimes());
} else {
log.debug("Decrementing retries of JobRetryStrategy '" + failedJobRetryTimeCycle+ "' for job " + job.getId());
}
job.setRetries(job.getRetries() - 1);
} catch (Exception e) {
throw new ActivitiException("failedJobRetryTimeCylcle has wrong format:" + failedJobRetryTimeCycle, exception);
}
}
if (exception != null) {
job.setExceptionMessage(exception.getMessage());
job.setExceptionStacktrace(getExceptionStacktrace());
}
// Dispatch both an update and a retry-decrement event
ActivitiEventDispatcher eventDispatcher = commandContext.getEventDispatcher();
if (eventDispatcher.isEnabled()) {
eventDispatcher.dispatchEvent(ActivitiEventBuilder.createEntityEvent(
ActivitiEventType.ENTITY_UPDATED, job));
eventDispatcher.dispatchEvent(ActivitiEventBuilder.createEntityEvent(
ActivitiEventType.JOB_RETRIES_DECREMENTED, job));
}
if (processEngineConfig.isAsyncExecutorEnabled() == false) {
JobExecutor jobExecutor = processEngineConfig.getJobExecutor();
JobAddedNotification messageAddedNotification = new JobAddedNotification(jobExecutor);
TransactionContext transactionContext = commandContext.getTransactionContext();
transactionContext.addTransactionListener(TransactionState.COMMITTED, messageAddedNotification);
}
return null;
}
protected Date calculateDueDate(CommandContext commandContext, int waitTimeInSeconds, Date oldDate) {
Calendar newDateCal = new GregorianCalendar();
if (oldDate != null) {
newDateCal.setTime(oldDate);
} else {
newDateCal.setTime(commandContext.getProcessEngineConfiguration().getClock().getCurrentTime());
}
newDateCal.add(Calendar.SECOND, waitTimeInSeconds);
return newDateCal.getTime();
}
private ActivityImpl getCurrentActivity(CommandContext commandContext, JobEntity job) {
String type = job.getJobHandlerType();
ActivityImpl activity = null;
if (TimerExecuteNestedActivityJobHandler.TYPE.equals(type) ||
TimerCatchIntermediateEventJobHandler.TYPE.equals(type)) {
ExecutionEntity execution = fetchExecutionEntity(commandContext, job.getExecutionId());
if (execution != null) {
activity = execution.getProcessDefinition().findActivity(job.getJobHandlerConfiguration());
}
} else if (TimerStartEventJobHandler.TYPE.equals(type)) {
DeploymentManager deploymentManager = commandContext.getProcessEngineConfiguration().getDeploymentManager();
ProcessDefinitionEntity processDefinition =
deploymentManager.findDeployedLatestProcessDefinitionByKeyAndTenantId(job.getJobHandlerConfiguration(), job.getTenantId());
if (processDefinition != null) {
activity = processDefinition.getInitial();
}
} else if (AsyncContinuationJobHandler.TYPE.equals(type)) {
ExecutionEntity execution = fetchExecutionEntity(commandContext, job.getExecutionId());
if (execution != null) {
activity = execution.getActivity();
}
} else {
// nop, because activity type is not supported
}
return activity;
}
private String getExceptionStacktrace() {
StringWriter stringWriter = new StringWriter();
exception.printStackTrace(new PrintWriter(stringWriter));
return stringWriter.toString();
}
private ExecutionEntity fetchExecutionEntity(CommandContext commandContext, String executionId) {
return commandContext.getExecutionEntityManager().findExecutionById(executionId);
}
}