/*
* Created on 28/mag/2010
*
* Copyright 2010 by Andrea Vacondio (andrea.vacondio@gmail.com).
*
* This file is part of the Sejda source code
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sejda.core.service;
import static java.util.Optional.ofNullable;
import static org.sejda.core.notification.dsl.ApplicationEventsNotifier.notifyEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.validation.ConstraintViolation;
import org.sejda.core.context.DefaultSejdaContext;
import org.sejda.core.context.SejdaContext;
import org.sejda.core.validation.DefaultValidationContext;
import org.sejda.model.exception.InvalidTaskParametersException;
import org.sejda.model.exception.TaskException;
import org.sejda.model.parameter.base.TaskParameters;
import org.sejda.model.task.CancellationOption;
import org.sejda.model.task.NotifiableTaskMetadata;
import org.sejda.model.task.TaskExecutionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Default implementation of the {@link TaskExecutionService}.
*
* @author Andrea Vacondio
*
*/
public final class DefaultTaskExecutionService implements TaskExecutionService {
private static final Logger LOG = LoggerFactory.getLogger(DefaultTaskExecutionService.class);
private final SejdaContext context = new DefaultSejdaContext();
@Override
public void execute(TaskParameters parameters) {
execute(parameters, new CancellationOption());
}
@Override
public void execute(TaskParameters parameters, CancellationOption cancellationOption) {
TaskExecutionContext executionContext = null;
LOG.trace("Starting execution for {}", parameters);
try {
validate(parameters);
executionContext = new TaskExecutionContext(context.getTask(parameters), parameters.isLenient());
cancellationOption.setExecutionContext(executionContext);
LOG.info("Starting task ({}) execution.", executionContext.task());
preExecution(executionContext);
actualExecution(parameters, executionContext);
postExecution(executionContext);
} catch (InvalidTaskParametersException i) {
LOG.error("Task execution failed due to invalid parameters: " + String.join(". ", i.getReasons()), i);
executionFailed(i, executionContext);
} catch (TaskException e) {
LOG.error(String.format("Task (%s) execution failed.",
ofNullable(executionContext).map(c -> c.task().toString()).orElse("")), e);
executionFailed(e, executionContext);
} catch (RuntimeException e) {
executionFailed(e, executionContext);
throw e;
}
}
private void executionFailed(Exception e, TaskExecutionContext executionContext) {
if (executionContext == null) {
notifyEvent(NotifiableTaskMetadata.NULL).taskFailed(e);
} else {
notifyEvent(executionContext.notifiableTaskMetadata()).taskFailed(e);
}
}
private void validate(TaskParameters parameters) throws InvalidTaskParametersException {
if (context.isValidation()) {
LOG.debug("Validating parameters.");
Set<ConstraintViolation<TaskParameters>> violations = DefaultValidationContext.getContext().getValidator()
.validate(parameters);
if (!violations.isEmpty()) {
StringBuilder sb = new StringBuilder(
String.format("Input parameters (%s) are not valid: ", parameters));
List<String> reasons = new ArrayList<>();
for (ConstraintViolation<TaskParameters> violation : violations) {
sb.append(String.format("\"(%s=%s) %s\" ", violation.getPropertyPath(), violation.getInvalidValue(),
violation.getMessage()));
reasons.add(violation.getMessage());
}
throw new InvalidTaskParametersException(sb.toString(), reasons);
}
} else {
LOG.info("Validation skipped.");
}
}
/**
* operations needed before the actual execution
*/
private void preExecution(TaskExecutionContext context) {
context.taskStart();
notifyEvent(context.notifiableTaskMetadata()).taskStarted();
}
/**
* operations needed after the actual execution
*/
private void postExecution(TaskExecutionContext context) {
context.taskEnded();
notifyEvent(context.notifiableTaskMetadata()).taskCompleted(context.executionTime());
}
/**
* actual execution of the task
*
* @param parameters
* @param task
* @throws TaskException
*/
@SuppressWarnings("unchecked")
private void actualExecution(TaskParameters parameters, TaskExecutionContext executionContext)
throws TaskException {
try {
executionContext.task().before(parameters, executionContext);
executionContext.task().execute(parameters);
} finally {
try {
executionContext.task().after();
} catch (RuntimeException e) {
LOG.warn("An unexpected error occurred during the execution of the 'after' phase.", e);
}
}
}
}