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}