package io.eguan.dtx;
/*
* #%L
* Project eguan
* %%
* Copyright (C) 2012 - 2017 Oodrive
* %%
* 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.
* #L%
*/
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import javax.annotation.Nonnull;
/**
* Class holding a task and the object associated to the completion of the task.
*
* @author oodrive
* @author llambert
* @author pwehrle
* @author ebredzinski
*
* @param <V>
* Object created by the task or related to the completion of the task.
*/
public abstract class DtxTaskFuture<V> implements Future<V> {
/** Task followed by this future. */
private final UUID taskId;
/** {@link DtxTaskApi} handling this task. */
private final DtxTaskApi dtxTaskApi;
/** true when the task is cancelled. */
private boolean cancelled;
/**
* Internal constructor for implementing classses.
*
* @param taskId
* the {@link UUID} of the task
* @param dtxTaskApi
* the target {@link DtxTaskApi} instance
*/
protected DtxTaskFuture(@Nonnull final UUID taskId, @Nonnull final DtxTaskApi dtxTaskApi) {
super();
this.taskId = Objects.requireNonNull(taskId);
this.dtxTaskApi = Objects.requireNonNull(dtxTaskApi);
}
/**
* Gets the UUID of the task held by this.
*
* @return the UUID of the task.
*/
public final UUID getTaskId() {
return taskId;
}
@Override
public final boolean cancel(final boolean mayInterruptIfRunning) {
// Cannot cancel the task once again
if (cancelled) {
return false;
}
cancelled = dtxTaskApi.cancel(taskId);
return cancelled;
}
@Override
public final boolean isCancelled() {
// TODO Should check if the task have been cancelled from elsewhere
return cancelled;
}
@Override
public final boolean isDone() {
final DtxTaskAdm task = dtxTaskApi.getTask(taskId);
final DtxTaskStatus status = task.getStatus();
return status.isDone();
}
private static final long DTX_TASK_END_SLEEP_TIME = 100; // ms
/**
* Wait for the end of the task for the given duration.
*
* @param duration
* maximum wait duration, in milliseconds. <code>0</code> to wait forever.
* @return <code>true</code> if the task is done, <code>false</code> if a timeout occurred.
* @throws InterruptedException
* if interrupted while waiting
* @throws ExecutionException
* if the task fails
*/
protected final boolean waitTaskEnd(final long duration) throws InterruptedException, ExecutionException {
final long end = duration == 0 ? Long.MAX_VALUE : System.currentTimeMillis() + duration;
do {
final DtxTaskStatus status = dtxTaskApi.getTask(taskId).getStatus();
if (status == DtxTaskStatus.COMMITTED) {
// Success
return true;
}
else if (status == DtxTaskStatus.ROLLED_BACK || status == DtxTaskStatus.UNKNOWN) {
// Failure TODO should get some exception from the transaction executor/resource manager
throw new ExecutionException(new IllegalStateException("Task failed, status=" + status));
}
Thread.sleep(DTX_TASK_END_SLEEP_TIME);
} while (end >= System.currentTimeMillis());
// Timeout
return false;
}
}