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