/* This file is part of VoltDB. * Copyright (C) 2008-2017 VoltDB Inc. * * 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 VoltDB. If not, see <http://www.gnu.org/licenses/>. */ package org.voltdb.jdbc; import java.util.concurrent.CancellationException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import org.voltdb.client.ClientResponse; /** * Provides a Future wrapper around an execution task's ClientResponse response object. * * @author Seb Coursol (copied and renamed from exampleutils) * @since 2.0 */ public class JDBC4ExecutionFuture implements Future<ClientResponse> { private final CountDownLatch latch = new CountDownLatch(1); private final long timeout; private final AtomicInteger status = new AtomicInteger(0); private static final int STATUS_RUNNING = 0; private static final int STATUS_SUCCESS = 1; private static final int STATUS_TIMEOUT = 2; private static final int STATUS_FAILURE = 3; private static final int STATUS_ABORTED = 4; private ClientResponse response = null; /** * Create a {@link Future} wrapper with the specified default timeout. * * @param timeout * the default timeout for the execution call. */ protected JDBC4ExecutionFuture(long timeout) { this.timeout = timeout; } /** * Sets the result of the operation and flag the execution call as completed. * * @param response * the execution call's response sent back by the database. */ protected void set(ClientResponse response) { if (!this.status.compareAndSet(STATUS_RUNNING, STATUS_SUCCESS)) return; this.response = response; this.latch.countDown(); } /** * Attempts to cancel execution of this task (not supported - will always return * <code>false</code>). * * @param mayInterruptIfRunning * true if the thread executing this task should be interrupted; otherwise, * in-progress tasks are allowed to complete. This flag is ignored since VoltDB does * not support cancellation of posted transaction requests. * @return <code>false</code> always: VoltDB does not support cancellation of posted transaction * requests. */ @Override public boolean cancel(boolean mayInterruptIfRunning) { return false; } /** * Waits if necessary for the computation to complete, and then retrieves its result. * * @return the computed result. * @throws CancellationException * if the computation was cancelled. * @throws ExecutionException * if the computation threw an exception. * @throws InterruptedException * if the current thread was interrupted while waiting. */ @Override public ClientResponse get() throws InterruptedException, ExecutionException { try { return get(this.timeout, TimeUnit.MILLISECONDS); } catch (TimeoutException to) { this.status.compareAndSet(STATUS_RUNNING, STATUS_TIMEOUT); throw new ExecutionException(to); } } /** * Waits if necessary for at most the given time for the computation to complete, and then * retrieves its result, if available. * * @param timeout * the maximum time to wait. * @param unit * the time unit of the timeout argument . * @return the computed result. * @throws CancellationException * if the computation was cancelled. * @throws ExecutionException * if the computation threw an exception. * @throws InterruptedException * if the current thread was interrupted while waiting. * @throws TimeoutException * if the wait timed out */ @Override public ClientResponse get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { if (!this.latch.await(timeout, unit)) { this.status.compareAndSet(STATUS_RUNNING, STATUS_TIMEOUT); throw new TimeoutException(); } if (isCancelled()) { throw new CancellationException(); } else if (this.response.getStatus() != ClientResponse.SUCCESS) { this.status.compareAndSet(STATUS_RUNNING, STATUS_FAILURE); throw new ExecutionException(new Exception(response.getStatusString())); } else { this.status.compareAndSet(STATUS_RUNNING, STATUS_SUCCESS); return this.response; } } /** * Returns <code>true</code> if this task was cancelled before it completed normally. * * @return <code>true</code> if this task was cancelled before it completed. */ @Override public boolean isCancelled() { return this.status.get() == STATUS_ABORTED; } /** * Returns <code>true</code> if this task completed. Completion may be due to normal * termination, an exception, or cancellation -- in all of these cases, this method will return * true. * * @return <code>true</code> if this task completed. */ @Override public boolean isDone() { return this.status.get() != STATUS_RUNNING; } }