/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2009 Sun Microsystems, Inc. */ package com.sun.opends.sdk.util; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.AbstractQueuedSynchronizer; import org.opends.sdk.*; import org.opends.sdk.responses.Responses; import org.opends.sdk.responses.Result; /** * This class provides a skeletal implementation of the {@code * FutureResult} interface, to minimize the effort required to implement * this interface. *

* This {@code FutureResult} implementation provides the following * features: *

* * @param * The type of result returned by this completion future. */ public abstract class AbstractFutureResult implements FutureResult, ResultHandler { @SuppressWarnings("serial") private final class Sync extends AbstractQueuedSynchronizer { // State value representing the initial state before a result has // been received. private static final int WAITING = 0; // State value representing that a result has been received and is // being processed. private static final int PENDING = 1; // State value representing that the request was cancelled. private static final int CANCELLED = 2; // State value representing that the request has failed. private static final int FAIL = 3; // State value representing that the request has succeeded. private static final int SUCCESS = 4; // These do not need to be volatile since their values are published // by updating the state after they are set and reading the state // immediately before they are read. private ErrorResultException errorResult = null; private M result = null; private M get0() throws ErrorResultException { if (errorResult != null) { // State must be FAILED or CANCELLED. throw errorResult; } else { // State must be SUCCESS. return result; } } private boolean setStatePending() { for (;;) { final int s = getState(); if (s != WAITING) { return false; } if (compareAndSetState(s, PENDING)) { return true; } } } /** * Allow all threads to acquire if future has completed. */ protected int tryAcquireShared(int ignore) { return innerIsDone() ? 1 : -1; } /** * Signal that the future has completed and threads waiting on get() * can be released. */ protected boolean tryReleaseShared(int finalState) { // Ensures that errorResult/result is published. setState(finalState); return true; } boolean innerCancel(boolean mayInterruptIfRunning) { if (!setStatePending()) { return false; } // Perform implementation defined cancellation. ErrorResultException errorResult = handleCancelRequest(mayInterruptIfRunning); if (errorResult == null) { final Result result = Responses .newResult(ResultCode.CLIENT_SIDE_USER_CANCELLED); errorResult = ErrorResultException.wrap(result); } this.errorResult = errorResult; try { // Invoke error result completion handler. if (handler != null) { handler.handleErrorResult(errorResult); } } finally { releaseShared(CANCELLED); // Publishes errorResult. } return true; } M innerGet() throws ErrorResultException, InterruptedException { acquireSharedInterruptibly(0); return get0(); } M innerGet(long nanosTimeout) throws ErrorResultException, TimeoutException, InterruptedException { if (!tryAcquireSharedNanos(0, nanosTimeout)) { throw new TimeoutException(); } else { return get0(); } } boolean innerIsCancelled() { return getState() == CANCELLED; } boolean innerIsDone() { return getState() > 1; } void innerSetErrorResult(ErrorResultException errorResult) { if (setStatePending()) { this.errorResult = errorResult; try { // Invoke error result completion handler. if (handler != null) { handler.handleErrorResult(errorResult); } } finally { releaseShared(FAIL); // Publishes errorResult. } } } void innerSetResult(M result) { if (setStatePending()) { this.result = result; try { // Invoke result completion handler. if (handler != null) { handler.handleResult(result); } } finally { releaseShared(SUCCESS); // Publishes result. } } } } private final Sync sync = new Sync(); private final ResultHandler handler; /** * Creates a new abstract future result with the provided result * handler. * * @param handler * A result handler which will be forwarded the result or * error when it arrives. */ protected AbstractFutureResult(ResultHandler handler) { this.handler = handler; } /** * {@inheritDoc} */ public final boolean cancel(boolean mayInterruptIfRunning) { return sync.innerCancel(mayInterruptIfRunning); } /** * {@inheritDoc} */ public final M get() throws ErrorResultException, InterruptedException { return sync.innerGet(); } /** * {@inheritDoc} */ public final M get(long timeout, TimeUnit unit) throws ErrorResultException, TimeoutException, InterruptedException { return sync.innerGet(unit.toNanos(timeout)); } /** * Sets the error result associated with this future. If ({@code * isDone() == true}) then the error result will be ignored, otherwise * the result handler will be invoked if one was provided and, on * return, any threads waiting on {@link #get} will be released and * the provided error result will be thrown. * * @param errorResult * The error result. */ public final void handleErrorResult(ErrorResultException errorResult) { sync.innerSetErrorResult(errorResult); } /** * Sets the result associated with this future. If ({@code isDone() == * true}) then the result will be ignored, otherwise the result * handler will be invoked if one was provided and, on return, any * threads waiting on {@link #get} will be released and the provided * result will be returned. * * @param result * The result. */ public final void handleResult(M result) { sync.innerSetResult(result); } /** * {@inheritDoc} */ public final boolean isCancelled() { return sync.innerIsCancelled(); } /** * {@inheritDoc} */ public final boolean isDone() { return sync.innerIsDone(); } /** * Invoked when {@link #cancel} is called and {@code isDone() == * false} and immediately before any threads waiting on {@link #get} * are released. Implementations may choose to return a custom error * result if needed or return {@code null} if the following default * error result is acceptable: * *
   * Result result = Responses
   *     .newResult(ResultCode.CLIENT_SIDE_USER_CANCELLED);
   * 
* * In addition, implementations may perform other cleanup, for * example, by issuing an LDAP abandon request. The default * implementation is to do nothing. * * @param mayInterruptIfRunning * {@code true} if the thread executing executing the * response handler should be interrupted; otherwise, * in-progress response handlers are allowed to complete. * @return The custom error result, or {@code null} if the default is * acceptable. */ protected ErrorResultException handleCancelRequest( boolean mayInterruptIfRunning) { // Do nothing by default. return null; } }