/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* 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.jbpm.executor.commands;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.EntityManagerFactory;
import org.jbpm.process.core.timer.DateTimeUtils;
import org.jbpm.runtime.manager.impl.jpa.EntityManagerFactoryManager;
import org.jbpm.shared.services.impl.TransactionalCommandService;
import org.jbpm.shared.services.impl.commands.UpdateStringCommand;
import org.kie.api.executor.Command;
import org.kie.api.executor.CommandContext;
import org.kie.api.executor.ExecutionResults;
import org.kie.api.executor.Reoccurring;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Execution error clean up command that aims at doing house keeping of execution error table used in jBPM:
* Command by default is auto configured to run once a day from the time it was initially scheduled though it can be reconfigured
* in terms of frequency when it is executed and if it shall run multiple times at all.<br/>
* Following is a complete list of accepted parameters:
* <ul>
* <li>DateFormat - date format for further date related params - if not given yyyy-MM-dd is used (pattern of SimpleDateFormat class)</li>
* <li>EmfName - name of entity manager factory to be used for queries (valid persistence unit name)</li>
* <li>SingleRun - indicates if execution should be single run only (true|false)</li>
* <li>NextRun - provides next execution time (valid time expression e.g. 1d, 5h, etc)</li>
* <li>OlderThan - indicates what errors should be deleted - older than given date</li>
* <li>OlderThanPeriod - indicated what errors should be deleted older than given time expression (valid time expression e.g. 1d, 5h, etc)</li>
* <li>ForProcess - indicates errors to be deleted only for given process definition</li>
* <li>ForProcessInstance - indicates errors to be deleted only for given process instance</li>
* <li>ForDeployment - indicates errors to be deleted that are from given deployment id</li>
* </ul>
*/
public class ExecutionErrorCleanupCommand implements Command, Reoccurring {
private static final Logger logger = LoggerFactory.getLogger(ExecutionErrorCleanupCommand.class);
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
private long nextScheduleTimeAdd = 24 * 60 * 60 * 1000; // one day in milliseconds
@Override
public Date getScheduleTime() {
if (nextScheduleTimeAdd < 0) {
return null;
}
long current = System.currentTimeMillis();
Date nextSchedule = new Date(current + nextScheduleTimeAdd);
logger.debug("Next schedule for job {} is set to {}", this.getClass().getSimpleName(), nextSchedule);
return nextSchedule;
}
@Override
public ExecutionResults execute(CommandContext ctx) throws Exception {
SimpleDateFormat formatToUse = DATE_FORMAT;
String dataFormat = (String) ctx.getData("DateFormat");
if (dataFormat != null) {
formatToUse = new SimpleDateFormat(dataFormat);
}
ExecutionResults executionResults = new ExecutionResults();
String emfName = (String)ctx.getData("EmfName");
if (emfName == null) {
emfName = "org.jbpm.domain";
}
String singleRun = (String)ctx.getData("SingleRun");
if ("true".equalsIgnoreCase(singleRun)) {
// disable rescheduling
this.nextScheduleTimeAdd = -1;
}
String nextRun = (String)ctx.getData("NextRun");
if (nextRun != null) {
nextScheduleTimeAdd = DateTimeUtils.parseDateAsDuration(nextRun);
}
// get hold of persistence and create instance of audit service
EntityManagerFactory emf = EntityManagerFactoryManager.get().getOrCreate(emfName);
// collect parameters
String olderThan = (String)ctx.getData("OlderThan");
String olderThanPeriod = (String)ctx.getData("OlderThanPeriod");
String forProcess = (String)ctx.getData("ForProcess");
String forProcessInstance = (String)ctx.getData("ForProcessInstance");
String forDeployment = (String)ctx.getData("ForDeployment");
if (olderThanPeriod != null) {
long olderThanDuration = DateTimeUtils.parseDateAsDuration(olderThanPeriod);
Date olderThanDate = new Date(System.currentTimeMillis() - olderThanDuration);
olderThan = formatToUse.format(olderThanDate);
}
Map<String, Object> parameters = new HashMap<>();
StringBuilder cleanUpErrorsQuery = new StringBuilder();
cleanUpErrorsQuery.append("delete from ExecutionErrorInfo where processInstanceId in "
+ "(select processInstanceId from ProcessInstanceLog where status in (2,3))");
if (olderThan != null && !olderThan.isEmpty()) {
cleanUpErrorsQuery.append(" and errorDate < :olderThan");
parameters.put("olderThan", formatToUse.parse(olderThan));
}
if (forProcess != null && !forProcess.isEmpty()) {
cleanUpErrorsQuery.append(" and processId = :forProcess");
parameters.put("forProcess", forProcess);
}
if (forProcessInstance != null && !forProcessInstance.isEmpty()) {
cleanUpErrorsQuery.append(" and processInstanceId = :forProcessInstance");
parameters.put("forProcessInstance", Long.parseLong(forProcessInstance));
}
if (forDeployment != null && !forDeployment.isEmpty()) {
cleanUpErrorsQuery.append(" and deploymentId = :forDeployment");
parameters.put("forDeployment", forDeployment);
}
TransactionalCommandService commandService = new TransactionalCommandService(emf);
int deletedErrors = commandService.execute(new UpdateStringCommand(cleanUpErrorsQuery.toString(), parameters));
logger.debug("Number of Execution errors deleted {}", deletedErrors);
executionResults.setData("ErrorsDeleted", deletedErrors);
return executionResults;
}
}