/* 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.persistence.entity; import java.util.Arrays; import java.util.Date; import java.util.List; import org.activiti.engine.ActivitiException; import org.activiti.engine.delegate.Expression; import org.activiti.engine.delegate.VariableScope; import org.activiti.engine.impl.calendar.BusinessCalendar; import org.activiti.engine.impl.calendar.CycleBusinessCalendar; import org.activiti.engine.impl.context.Context; import org.activiti.engine.impl.el.NoExecutionVariableScope; import org.activiti.engine.impl.interceptor.CommandContext; import org.activiti.engine.impl.jobexecutor.TimerCatchIntermediateEventJobHandler; import org.activiti.engine.impl.jobexecutor.TimerDeclarationImpl; import org.activiti.engine.impl.jobexecutor.TimerEventHandler; import org.activiti.engine.impl.jobexecutor.TimerExecuteNestedActivityJobHandler; import org.activiti.engine.impl.jobexecutor.TimerStartEventJobHandler; import org.activiti.engine.impl.pvm.process.ActivityImpl; import org.activiti.engine.repository.ProcessDefinition; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Tom Baeyens */ public class TimerEntity extends JobEntity { private static final long serialVersionUID = 1L; private static Logger log = LoggerFactory.getLogger(TimerEntity.class); protected int maxIterations; protected String repeat; protected Date endDate; public TimerEntity() { super(); this.jobType = "timer"; } public TimerEntity(TimerDeclarationImpl timerDeclaration) { jobHandlerType = timerDeclaration.getJobHandlerType(); jobHandlerConfiguration = timerDeclaration.getJobHandlerConfiguration(); isExclusive = timerDeclaration.isExclusive(); repeat = timerDeclaration.getRepeat(); retries = timerDeclaration.getRetries(); this.jobType = "timer"; } private TimerEntity(TimerEntity te) { jobHandlerConfiguration = te.jobHandlerConfiguration; jobHandlerType = te.jobHandlerType; isExclusive = te.isExclusive; repeat = te.repeat; retries = te.retries; endDate = te.endDate; executionId = te.executionId; processInstanceId = te.processInstanceId; processDefinitionId = te.processDefinitionId; // Inherit tenant tenantId = te.tenantId; this.jobType = "timer"; } @Override public void execute(CommandContext commandContext) { //set endDate if it was set to the definition restoreExtraData(commandContext, jobHandlerConfiguration); if (this.getDuedate() != null && !isValidTime(this.getDuedate())) { if (log.isDebugEnabled()) { log.debug("Timer {} fired. but the dueDate is after the endDate. Deleting timer.", getId()); } delete(); return; } super.execute(commandContext); if (log.isDebugEnabled()) { log.debug("Timer {} fired. Deleting timer.", getId()); } delete(); if (repeat != null) { int repeatValue = calculateRepeatValue(); if (repeatValue != 0) { if (repeatValue > 0) { setNewRepeat(repeatValue); } Date newTimer = calculateNextTimer(); if (newTimer != null && isValidTime(newTimer)) { TimerEntity te = new TimerEntity(this); te.setDuedate(newTimer); Context.getCommandContext().getJobEntityManager().schedule(te); } } } } protected void restoreExtraData(CommandContext commandContext, String jobHandlerConfiguration) { String embededActivityId = jobHandlerConfiguration; if (jobHandlerType.equalsIgnoreCase(TimerExecuteNestedActivityJobHandler.TYPE) || jobHandlerType.equalsIgnoreCase(TimerCatchIntermediateEventJobHandler.TYPE) || jobHandlerType.equalsIgnoreCase(TimerStartEventJobHandler.TYPE)) { embededActivityId = TimerEventHandler.getActivityIdFromConfiguration(jobHandlerConfiguration); String endDateExpressionString = TimerEventHandler.getEndDateFromConfiguration(jobHandlerConfiguration); if (endDateExpressionString!=null) { Expression endDateExpression = Context.getProcessEngineConfiguration().getExpressionManager().createExpression(endDateExpressionString); String endDateString = null; BusinessCalendar businessCalendar = Context.getProcessEngineConfiguration().getBusinessCalendarManager() .getBusinessCalendar(getBusinessCalendarName(TimerEventHandler.geCalendarNameFromConfiguration(jobHandlerConfiguration))); VariableScope executionEntity = null; if (executionId != null) { executionEntity = commandContext.getExecutionEntityManager().findExecutionById(executionId); } if (executionEntity == null) { executionEntity = NoExecutionVariableScope.getSharedInstance(); } if (endDateExpression != null) { Object endDateValue = endDateExpression.getValue(executionEntity); if (endDateValue instanceof String) { endDateString = (String) endDateValue; } else if (endDateValue instanceof Date) { endDate = (Date) endDateValue; } else { throw new ActivitiException("Timer '" + ((ExecutionEntity) executionEntity).getActivityId() + "' was not configured with a valid duration/time, either hand in a java.util.Date or a String in format 'yyyy-MM-dd'T'hh:mm:ss'"); } if (endDate == null) { endDate = businessCalendar.resolveEndDate(endDateString); } } } } if (processDefinitionId != null) { ProcessDefinition definition = commandContext.getProcessEngineConfiguration() .getDeploymentManager().findDeployedProcessDefinitionById(processDefinitionId); maxIterations = checkStartEventDefinitions(definition, embededActivityId); if (maxIterations <= 1) { maxIterations = checkBoundaryEventsDefinitions(definition, embededActivityId); } } else { maxIterations = 1; } } protected int checkStartEventDefinitions(ProcessDefinition def, String embededActivityId) { List<TimerDeclarationImpl> startTimerDeclarations = (List<TimerDeclarationImpl>) ((ProcessDefinitionEntity) def).getProperty("timerStart"); if (startTimerDeclarations != null && startTimerDeclarations.size() > 0) { TimerDeclarationImpl timerDeclaration = null; for (TimerDeclarationImpl startTimerDeclaration : startTimerDeclarations) { String definitionActivityId = TimerEventHandler.getActivityIdFromConfiguration(startTimerDeclaration.getJobHandlerConfiguration()); if (startTimerDeclaration.getJobHandlerType().equalsIgnoreCase(jobHandlerType) && (definitionActivityId.equalsIgnoreCase(embededActivityId))) { timerDeclaration = startTimerDeclaration; } } if (timerDeclaration != null) { return calculateMaxIterationsValue(timerDeclaration.getDescription().getExpressionText()); } } return 1; } protected int checkBoundaryEventsDefinitions(ProcessDefinition def, String embededActivityId) { return checkBoundaryEventsDefinitions(((ProcessDefinitionEntity) def).getActivities(), embededActivityId); } protected int checkBoundaryEventsDefinitions(List<ActivityImpl> activities, String embededActivityId) { // should check level by level, first check provided activities list for (ActivityImpl activity : activities) { List<TimerDeclarationImpl> activityTimerDeclarations = (List<TimerDeclarationImpl>) activity.getProperty("timerDeclarations"); if (activityTimerDeclarations != null) { for (TimerDeclarationImpl timerDeclaration : activityTimerDeclarations) { String definitionActivityId = TimerEventHandler.getActivityIdFromConfiguration(timerDeclaration.getJobHandlerConfiguration()); if (timerDeclaration.getJobHandlerType().equalsIgnoreCase(jobHandlerType) && (definitionActivityId.equalsIgnoreCase(embededActivityId))) { return calculateMaxIterationsValue(timerDeclaration.getDescription().getExpressionText()); } } } } // now check sub activities for (ActivityImpl activity : activities) { return checkBoundaryEventsDefinitions(activity.getActivities(), embededActivityId); } return 1; } protected int calculateMaxIterationsValue(String originalExpression) { int times = Integer.MAX_VALUE; List<String> expression = Arrays.asList(originalExpression.split("/")); if (expression.size() > 1 && expression.get(0).startsWith("R")) { times = Integer.MAX_VALUE; if (expression.get(0).length() > 1) { times = Integer.parseInt(expression.get(0).substring(1)); } } return times; } protected boolean isValidTime(Date newTimer) { BusinessCalendar businessCalendar = Context .getProcessEngineConfiguration() .getBusinessCalendarManager() .getBusinessCalendar(getBusinessCalendarName(TimerEventHandler.geCalendarNameFromConfiguration(jobHandlerConfiguration))); return businessCalendar.validateDuedate(repeat , maxIterations, endDate, newTimer); } protected int calculateRepeatValue() { int times = -1; List<String> expression = Arrays.asList(repeat.split("/")); if (expression.size() > 1 && expression.get(0).startsWith("R") && expression.get(0).length() > 1) { times = Integer.parseInt(expression.get(0).substring(1)); if (times > 0) { times--; } } return times; } protected void setNewRepeat(int newRepeatValue) { List<String> expression = Arrays.asList(repeat.split("/")); expression = expression.subList(1, expression.size()); StringBuilder repeatBuilder = new StringBuilder("R"); repeatBuilder.append(newRepeatValue); for (String value : expression) { repeatBuilder.append("/"); repeatBuilder.append(value); } repeat = repeatBuilder.toString(); } protected Date calculateNextTimer() { BusinessCalendar businessCalendar = Context .getProcessEngineConfiguration() .getBusinessCalendarManager() .getBusinessCalendar(getBusinessCalendarName(TimerEventHandler.geCalendarNameFromConfiguration(jobHandlerConfiguration))); return businessCalendar.resolveDuedate(repeat,maxIterations); } protected String getBusinessCalendarName(String calendarName) { String businessCalendarName = CycleBusinessCalendar.NAME; if (StringUtils.isNotEmpty(calendarName)) { VariableScope execution = NoExecutionVariableScope.getSharedInstance(); if (StringUtils.isNotEmpty(this.executionId)) { execution = Context.getCommandContext().getExecutionEntityManager().findExecutionById(this.executionId); } businessCalendarName = (String) Context.getProcessEngineConfiguration().getExpressionManager().createExpression(calendarName).getValue(execution); } return businessCalendarName; } public String getRepeat() { return repeat; } public void setRepeat(String repeat) { this.repeat = repeat; } public Date getEndDate() { return endDate; } public void setEndDate(Date endDate) { this.endDate = endDate; } }