package org.ovirt.engine.core.vdsbroker.vdsbroker;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
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.VDSError;
import org.ovirt.engine.core.common.interfaces.FutureVDSCall;
import org.ovirt.engine.core.common.vdscommands.VDSReturnValue;
import org.ovirt.engine.core.common.vdscommands.VdsIdVDSCommandParametersBase;
import org.ovirt.engine.core.vdsbroker.TransportRunTimeException;
/**
* This class is used for the non-blocking VDSM API. It uses Future API to fetch the response from the actual http
* connection and computes the return value when the the caller is fetching the VDSReturnValue.
*
*/
public abstract class FutureVDSCommand<P extends VdsIdVDSCommandParametersBase> extends VdsBrokerCommand<P> implements FutureVDSCall<VDSReturnValue> {
private static final String TIMEOUT_MESSAGE = "Internal timeout occured";
public FutureVDSCommand(P parameters) {
super(parameters);
}
protected Future<Map<String, Object>> httpTask;
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return httpTask.cancel(mayInterruptIfRunning);
}
@Override
public boolean isCancelled() {
return httpTask.isCancelled();
}
@Override
public boolean isDone() {
return httpTask.isDone();
}
/**
* get the return value and wait for the reply with default host connection timeout. We need to assure that this
* thread is released after a timeout so the blocking call for get is overridden here and delegated to the get(long
* timeout, TimeUnit unit)
*/
@Override
public VDSReturnValue get() {
try {
return get(Config.getValue(ConfigValues.vdsTimeout), TimeUnit.SECONDS);
} catch (TimeoutException e) {
return getVDSReturnValue();
}
}
/**
* Process the return value and reply back. When exceptions raises they will be logged and set a return value accordingly.
*
*/
@Override
public VDSReturnValue get(long timeout, TimeUnit unit) throws TimeoutException {
try {
status = new StatusOnlyReturn(httpTask.get(timeout, unit));
checkTimeout();
proceedProxyReturnValue();
} catch (TimeoutException e) {
httpTask.cancel(true);
VDSNetworkException ex = new VDSNetworkException("Timeout during rpc call");
ex.setVdsError(new VDSError(EngineError.VDS_NETWORK_ERROR, "Timeout during rpc call"));
setVdsRuntimeErrorAndReport(ex);
log.error("Timeout waiting for VDSM response: {}", e.getMessage());
log.trace("Exception", e);
throw e;
} catch (VDSNetworkException e) {
setVdsRuntimeErrorAndReport(e);
if (isPolicyResetMessage(e.getVdsError().getMessage())) {
log.info("Policy reset required for network reconfiguration");
} else {
log.error("Error: {}", e.getMessage());
log.error("Exception", e);
}
} catch (TransportRunTimeException e) {
handleTransportRunTimeException(e);
} catch (Exception e) {
handleGenericException(e);
}
return getVDSReturnValue();
}
private void handleGenericException(Exception e) {
log.error("Error: {}", e.getMessage());
log.error("Exception", e);
setVdsRuntimeErrorAndReport(e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e));
}
protected void handleTransportRunTimeException(TransportRunTimeException e) {
handleGenericException(e);
}
private void checkTimeout() throws TimeoutException {
String message = getReturnStatus().message;
if (TIMEOUT_MESSAGE.equals(message)) {
throw new TimeoutException(message);
}
}
@Override
protected abstract void executeVdsBrokerCommand();
}