/*
* 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:
*
* - The {@link #get} methods throw {@link ErrorResultException}s
* instead of the more generic {@code ExecutionException}s.
*
- The {@link #get} methods never throw {@code
* CancellationException} since requests in this SDK can usually be
* cancelled via other external means (e.g. the {@code Cancel} extended
* operation) for which there are well defined error results. Therefore
* cancellation is always signalled by throwing a
* {@link CancelledResultException} in order to be consistent with other
* error results.
*
- A {@link ResultHandler} can be provided to the constructor. The
* result handler will be invoked immediately after the result or error
* is received but before threads blocked on {@link #get} are released.
* More specifically, result handler invocation happens-before a
* call to {@link #get}. NOTE: a result handler which attempts to
* call {@link #get} will deadlock.
*
- Sub-classes may choose to implement specific cancellation cleanup
* by implementing the {@link #handleCancelRequest} method.
*
*
* @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 super M> 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 super M> 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;
}
}