package org.ovirt.engine.core.bll.tasks;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.ovirt.engine.core.bll.Backend;
import org.ovirt.engine.core.bll.CommandBase;
import org.ovirt.engine.core.bll.context.CommandContext;
import org.ovirt.engine.core.bll.utils.BackendUtils;
import org.ovirt.engine.core.common.action.VdcReturnValueBase;
import org.ovirt.engine.core.common.businessentities.CommandEntity;
import org.ovirt.engine.core.common.config.Config;
import org.ovirt.engine.core.common.config.ConfigValues;
import org.ovirt.engine.core.common.errors.EngineError;
import org.ovirt.engine.core.common.errors.EngineFault;
import org.ovirt.engine.core.compat.CommandStatus;
import org.ovirt.engine.core.compat.Guid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class CommandExecutor {
private static final ExecutorService executor =
Executors.newFixedThreadPool(Config.<Integer>getValue(ConfigValues.CommandCoordinatorThreadPoolSize));
private static final Logger log = LoggerFactory.getLogger(CommandExecutor.class);
private final CommandsRepository commandsRepository;
@Inject
CommandExecutor(CommandsRepository commandsRepository) {
this.commandsRepository = commandsRepository;
}
public Future<VdcReturnValueBase> executeAsyncCommand(final CommandBase<?> command,
final CommandContext cmdContext) {
Future<VdcReturnValueBase> retVal;
try {
retVal = executor.submit(() -> executeCommand(command, cmdContext));
} catch (RejectedExecutionException ex) {
command.setCommandStatus(CommandStatus.FAILED);
log.error("Failed to submit command to executor service, command '{}' status has been set to FAILED",
command.getCommandId());
retVal = new RejectedExecutionFuture();
}
return retVal;
}
private VdcReturnValueBase executeCommand(final CommandBase<?> command, final CommandContext cmdContext) {
VdcReturnValueBase result = BackendUtils.getBackendCommandObjectsHandler(log).runAction(command,
cmdContext != null ? cmdContext.getExecutionContext() : null);
updateCommandResult(command.getCommandId(), result);
return result;
}
private void updateCommandResult(final Guid commandId, final VdcReturnValueBase result) {
CommandEntity cmdEntity = commandsRepository.getCommandEntity(commandId);
cmdEntity.setReturnValue(result);
if (!result.isValid()) {
cmdEntity.setCommandStatus(CommandStatus.FAILED);
}
commandsRepository.persistCommand(cmdEntity);
}
static class RejectedExecutionFuture implements Future<VdcReturnValueBase> {
VdcReturnValueBase retValue;
RejectedExecutionFuture() {
retValue = new VdcReturnValueBase();
retValue.setSucceeded(false);
EngineFault fault = new EngineFault();
fault.setError(EngineError.ResourceException);
fault.setMessage(Backend.getInstance()
.getVdsErrorsTranslator()
.translateErrorTextSingle(fault.getError().toString()));
retValue.setFault(fault);
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return false;
}
@Override
public boolean isCancelled() {
return false;
}
@Override
public boolean isDone() {
return true;
}
@Override
public VdcReturnValueBase get() throws InterruptedException, ExecutionException {
return retValue;
}
@Override
public VdcReturnValueBase get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return retValue;
}
}
}