001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2014-2016 ForgeRock AS.
015 */
016
017package org.forgerock.opendj.ldap.spi;
018
019import org.forgerock.opendj.ldap.IntermediateResponseHandler;
020import org.forgerock.opendj.ldap.LdapException;
021import org.forgerock.opendj.ldap.LdapPromise;
022import org.forgerock.opendj.ldap.ResultCode;
023import org.forgerock.opendj.ldap.requests.Request;
024import org.forgerock.opendj.ldap.responses.IntermediateResponse;
025import org.forgerock.opendj.ldap.responses.Result;
026import org.forgerock.util.promise.Promise;
027import org.forgerock.util.promise.PromiseImpl;
028import org.forgerock.util.promise.Promises;
029
030import static org.forgerock.opendj.ldap.LdapException.*;
031
032/**
033 * This class provides an implementation of the {@link LdapPromise}.
034 *
035 * @param <R>
036 *            The type of the associated {@link Request}.
037 * @param <S>
038 *            The type of result returned by this promise.
039 * @see Promise
040 * @see Promises
041 * @see LdapPromise
042 */
043public abstract class ResultLdapPromiseImpl<R extends Request, S extends Result> extends LdapPromiseImpl<S>
044        implements LdapPromise<S>, IntermediateResponseHandler {
045    private final R request;
046    private IntermediateResponseHandler intermediateResponseHandler;
047    private volatile long timestamp;
048
049    ResultLdapPromiseImpl(final PromiseImpl<S, LdapException> impl, final int requestID, final R request,
050            final IntermediateResponseHandler intermediateResponseHandler) {
051        super(impl, requestID);
052        this.request = request;
053        this.intermediateResponseHandler = intermediateResponseHandler;
054        this.timestamp = System.currentTimeMillis();
055    }
056
057    @Override
058    public final boolean handleIntermediateResponse(final IntermediateResponse response) {
059        // FIXME: there's a potential race condition here - the promise could
060        // get cancelled between the isDone() call and the handler
061        // invocation. We'd need to add support for intermediate handlers in
062        // the synchronizer.
063        if (!isDone()) {
064            updateTimestamp();
065            if (intermediateResponseHandler != null
066                    && !intermediateResponseHandler.handleIntermediateResponse(response)) {
067                intermediateResponseHandler = null;
068            }
069        }
070        return true;
071    }
072
073    /**
074     * Returns {@code true} if this promise represents the result of a bind or
075     * StartTLS request. The default implementation is to return {@code false}.
076     *
077     * @return {@code true} if this promise represents the result of a bind or
078     *         StartTLS request.
079     */
080    public boolean isBindOrStartTLS() {
081        return false;
082    }
083
084    /**
085     * Returns a string representation of this promise's state.
086     *
087     * @return String representation of this promise's state.
088     */
089    @Override
090    public String toString() {
091        final StringBuilder sb = new StringBuilder();
092        sb.append("( requestID = ");
093        sb.append(getRequestID());
094        sb.append(" timestamp = ");
095        sb.append(timestamp);
096        sb.append(" request = ");
097        sb.append(getRequest());
098        sb.append(" )");
099        return sb.toString();
100    }
101
102    /**
103     * Sets the result associated to this promise as an error result.
104     *
105     * @param result
106     *            result of an operation
107     */
108    public final void adaptErrorResult(final Result result) {
109        final S errorResult = newErrorResult(result.getResultCode(), result.getDiagnosticMessage(), result.getCause());
110        setResultOrError(errorResult);
111    }
112
113    /**
114     * Returns the creation time of this promise.
115     *
116     * @return the timestamp indicating creation time of this promise
117     */
118    public final long getTimestamp() {
119        return timestamp;
120    }
121
122    abstract S newErrorResult(ResultCode resultCode, String diagnosticMessage, Throwable cause);
123
124    /**
125     * Sets the result associated to this promise.
126     *
127     * @param result
128     *            the result of operation
129     */
130    public final void setResultOrError(final S result) {
131        if (result.getResultCode().isExceptional()) {
132            getWrappedPromise().handleException(newLdapException(result));
133        } else {
134            getWrappedPromise().handleResult(result);
135        }
136    }
137
138    /**
139     * Returns the attached request.
140     *
141     * @return The request.
142     */
143    public R getRequest() {
144        return request;
145    }
146
147    final void updateTimestamp() {
148        timestamp = System.currentTimeMillis();
149    }
150
151    /**
152     * Returns {@code true} if this request should be canceled once the timeout
153     * period expires. The default implementation is to return {@code true}
154     * which will be appropriate for nearly all requests, the one obvious
155     * exception being persistent searches.
156     *
157     * @return {@code true} if this request should be canceled once the timeout
158     *         period expires.
159     */
160    public boolean checkForTimeout() {
161        return true;
162    }
163}