| | |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.ConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.FutureResult; |
| | | import org.forgerock.opendj.ldap.LdapPromise; |
| | | import org.forgerock.opendj.ldap.IntermediateResponseHandler; |
| | | import org.forgerock.opendj.ldap.requests.BindRequest; |
| | | import org.forgerock.opendj.ldap.responses.BindResult; |
| | |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public FutureResult<BindResult> bindAsync(BindRequest request, |
| | | public LdapPromise<BindResult> bindAsync(BindRequest request, |
| | | IntermediateResponseHandler intermediateResponseHandler) { |
| | | throw new UnsupportedOperationException(); |
| | | } |
| | |
| | | * associated with this connection. If re-authentication fails for some |
| | | * reason then this connection will be automatically closed. |
| | | * |
| | | * @return A future representing the result of the operation. |
| | | * @return A promise representing the result of the operation. |
| | | * @throws UnsupportedOperationException |
| | | * If this connection does not support rebind operations. |
| | | * @throws IllegalStateException |
| | | * If this connection has already been closed, i.e. if |
| | | * {@code isClosed() == true}. |
| | | */ |
| | | public FutureResult<BindResult> rebindAsync() { |
| | | public LdapPromise<BindResult> rebindAsync() { |
| | | if (request == null) { |
| | | throw new UnsupportedOperationException(); |
| | | } |
| | | |
| | | return (FutureResult<BindResult>) connection.bindAsync(request) |
| | | return (LdapPromise<BindResult>) connection.bindAsync(request) |
| | | .onSuccess(new SuccessHandler<BindResult>() { |
| | | @Override |
| | | public void handleResult(final BindResult result) { |
| | |
| | | |
| | | import static org.fest.assertions.Assertions.assertThat; |
| | | import static org.forgerock.opendj.ldap.Connections.newInternalConnection; |
| | | import static org.forgerock.opendj.ldap.LdapException.*; |
| | | |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | |
| | | Connection c = new AbstractConnectionWrapper<Connection>(newInternalConnection(backend)) { |
| | | @Override |
| | | public Result add(Entry entry) throws LdapException { |
| | | throw LdapException.newErrorResult(resultCodeOfThrownException); |
| | | throw newLdapException(resultCodeOfThrownException); |
| | | } |
| | | }; |
| | | ManagementContext ctx = |
| | |
| | | Connection c = new AbstractConnectionWrapper<Connection>(newInternalConnection(backend)) { |
| | | @Override |
| | | public SearchResultEntry readEntry(DN name, String... attributeDescriptions) throws LdapException { |
| | | throw LdapException.newErrorResult(resultCodeOfThrownException); |
| | | throw newLdapException(resultCodeOfThrownException); |
| | | } |
| | | }; |
| | | ManagementContext ctx = |
| | |
| | | <differenceType>8001</differenceType> |
| | | <justification>OPENDJ-1536 Rename FutureResult and ErrorResultException classes hierarchy in the SDK to enhance code consistency</justification> |
| | | </difference> |
| | | <difference> |
| | | <className>org/forgerock/opendj/ldap/*Connection*</className> |
| | | <differenceType>7006</differenceType> |
| | | <method>*Async*</method> |
| | | <to>org.forgerock.opendj.ldap.LdapPromise</to> |
| | | <justification>OPENDJ-1536 Rename FutureResult and ErrorResultException classes hierarchy in the SDK to enhance code consistency</justification> |
| | | </difference> |
| | | <difference> |
| | | <className>org/forgerock/opendj/ldap/RootDSE</className> |
| | | <differenceType>7006</differenceType> |
| | | <method>*Async*</method> |
| | | <to>org.forgerock.opendj.ldap.LdapPromise</to> |
| | | <justification>OPENDJ-1536 Rename FutureResult and ErrorResultException classes hierarchy in the SDK to enhance code consistency</justification> |
| | | </difference> |
| | | <difference> |
| | | <className>org/forgerock/opendj/ldap/schema/Schema*</className> |
| | | <differenceType>7006</differenceType> |
| | | <method>*Async*</method> |
| | | <to>org.forgerock.opendj.ldap.LdapPromise</to> |
| | | <justification>OPENDJ-1536 Rename FutureResult and ErrorResultException classes hierarchy in the SDK to enhance code consistency</justification> |
| | | </difference> |
| | | <difference> |
| | | <className>org/forgerock/opendj/ldap/Connection</className> |
| | | <differenceType>7012</differenceType> |
| | | <method>org.forgerock.opendj.ldap.LdapPromise *Async(*)</method> |
| | | <justification>OPENDJ-1536 Rename FutureResult and ErrorResultException classes hierarchy in the SDK to enhance code consistency</justification> |
| | | </difference> |
| | | <difference> |
| | | <className>org/forgerock/opendj/ldap/FutureResult</className> |
| | | <differenceType>8001</differenceType> |
| | | <justification>OPENDJ-1536 Rename FutureResult and ErrorResultException classes hierarchy in the SDK to enhance code consistency</justification> |
| | | </difference> |
| | | </differences> |
| | |
| | | return blockingGetOrThrow(searchAsync(request, handler)); |
| | | } |
| | | |
| | | private <T extends Result> T blockingGetOrThrow(FutureResult<T> future) throws LdapException { |
| | | private <T extends Result> T blockingGetOrThrow(LdapPromise<T> promise) throws LdapException { |
| | | try { |
| | | return future.getOrThrow(); |
| | | return promise.getOrThrow(); |
| | | } catch (InterruptedException e) { |
| | | throw interrupted(e); |
| | | } |
| | |
| | | |
| | | // Handle thread interruption. |
| | | private LdapException interrupted(InterruptedException e) { |
| | | return newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED, e); |
| | | return newLdapException(ResultCode.CLIENT_SIDE_USER_CANCELLED, e); |
| | | } |
| | | } |
| | |
| | | |
| | | import static org.forgerock.opendj.ldap.LdapException.*; |
| | | import static org.forgerock.opendj.ldap.requests.Requests.*; |
| | | import static org.forgerock.opendj.ldap.spi.LdapPromises.*; |
| | | |
| | | import static com.forgerock.opendj.ldap.CoreMessages.*; |
| | | |
| | |
| | | */ |
| | | private LdapException filterError(final LdapException error) { |
| | | if (error.getResult().getResultCode().equals(ResultCode.SIZE_LIMIT_EXCEEDED)) { |
| | | return newErrorResult(ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED, |
| | | return newLdapException(ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED, |
| | | ERR_UNEXPECTED_SEARCH_RESULT_ENTRIES_NO_COUNT.get().toString()); |
| | | } else { |
| | | return error; |
| | |
| | | private SearchResultEntry getSingleEntry() throws LdapException { |
| | | if (entryCount == 0) { |
| | | // Did not find any entries. |
| | | throw newErrorResult(ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED, |
| | | throw newLdapException(ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED, |
| | | ERR_NO_SEARCH_RESULT_ENTRIES.get().toString()); |
| | | } else if (entryCount > 1) { |
| | | // Got more entries than expected. |
| | | throw newErrorResult(ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED, |
| | | throw newLdapException(ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED, |
| | | ERR_UNEXPECTED_SEARCH_RESULT_ENTRIES.get(entryCount).toString()); |
| | | } else if (firstReference != null) { |
| | | // Got an unexpected search result reference. |
| | | throw newErrorResult(ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED, |
| | | throw newLdapException(ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED, |
| | | ERR_UNEXPECTED_SEARCH_RESULT_REFERENCES.get(firstReference.getURIs().iterator().next()) |
| | | .toString()); |
| | | } else { |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> addAsync(final AddRequest request) { |
| | | public LdapPromise<Result> addAsync(final AddRequest request) { |
| | | return addAsync(request, null); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> applyChangeAsync(ChangeRecord request) { |
| | | public LdapPromise<Result> applyChangeAsync(ChangeRecord request) { |
| | | return applyChangeAsync(request, null); |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> applyChangeAsync(final ChangeRecord request, |
| | | public LdapPromise<Result> applyChangeAsync(final ChangeRecord request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | final ChangeRecordVisitor<FutureResult<Result>, Connection> visitor = |
| | | new ChangeRecordVisitor<FutureResult<Result>, Connection>() { |
| | | final ChangeRecordVisitor<LdapPromise<Result>, Connection> visitor = |
| | | new ChangeRecordVisitor<LdapPromise<Result>, Connection>() { |
| | | |
| | | @Override |
| | | public FutureResult<Result> visitChangeRecord(final Connection p, final AddRequest change) { |
| | | public LdapPromise<Result> visitChangeRecord(final Connection p, final AddRequest change) { |
| | | return p.addAsync(change, intermediateResponseHandler); |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> visitChangeRecord(final Connection p, final DeleteRequest change) { |
| | | public LdapPromise<Result> visitChangeRecord(final Connection p, final DeleteRequest change) { |
| | | return p.deleteAsync(change, intermediateResponseHandler); |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> visitChangeRecord(final Connection p, final ModifyDNRequest change) { |
| | | public LdapPromise<Result> visitChangeRecord(final Connection p, final ModifyDNRequest change) { |
| | | return p.modifyDNAsync(change, intermediateResponseHandler); |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> visitChangeRecord(final Connection p, final ModifyRequest change) { |
| | | public LdapPromise<Result> visitChangeRecord(final Connection p, final ModifyRequest change) { |
| | | return p.modifyAsync(change, intermediateResponseHandler); |
| | | } |
| | | }; |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<BindResult> bindAsync(final BindRequest request) { |
| | | public LdapPromise<BindResult> bindAsync(final BindRequest request) { |
| | | return bindAsync(request, null); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<CompareResult> compareAsync(final CompareRequest request) { |
| | | public LdapPromise<CompareResult> compareAsync(final CompareRequest request) { |
| | | return compareAsync(request, null); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> deleteAsync(final DeleteRequest request) { |
| | | public LdapPromise<Result> deleteAsync(final DeleteRequest request) { |
| | | return deleteAsync(request, null); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | @Override |
| | | public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(final ExtendedRequest<R> request) { |
| | | public <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(final ExtendedRequest<R> request) { |
| | | return extendedRequestAsync(request, null); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> modifyAsync(final ModifyRequest request) { |
| | | public LdapPromise<Result> modifyAsync(final ModifyRequest request) { |
| | | return modifyAsync(request, null); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request) { |
| | | public LdapPromise<Result> modifyDNAsync(final ModifyDNRequest request) { |
| | | return modifyDNAsync(request, null); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<SearchResultEntry> readEntryAsync(final DN name, |
| | | public LdapPromise<SearchResultEntry> readEntryAsync(final DN name, |
| | | final Collection<String> attributeDescriptions) { |
| | | final SearchRequest request = Requests.newSingleEntrySearchRequest(name, SearchScope.BASE_OBJECT, |
| | | Filter.objectClassPresent()); |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> searchAsync(final SearchRequest request, final SearchResultHandler resultHandler) { |
| | | public LdapPromise<Result> searchAsync(final SearchRequest request, final SearchResultHandler resultHandler) { |
| | | return searchAsync(request, null, resultHandler); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<SearchResultEntry> searchSingleEntryAsync(final SearchRequest request) { |
| | | public LdapPromise<SearchResultEntry> searchSingleEntryAsync(final SearchRequest request) { |
| | | final SingleEntryHandler handler = new SingleEntryHandler(); |
| | | return FutureResultWrapper.asFutureResult(searchAsync(enforceSingleEntrySearchRequest(request), handler).then( |
| | | new Function<Result, SearchResultEntry, LdapException>() { |
| | | @Override |
| | | public SearchResultEntry apply(final Result value) throws LdapException { |
| | | return handler.getSingleEntry(); |
| | | } |
| | | }, new Function<LdapException, SearchResultEntry, LdapException>() { |
| | | @Override |
| | | public SearchResultEntry apply(final LdapException error) throws LdapException { |
| | | throw handler.filterError(error); |
| | | } |
| | | })); |
| | | return asPromise(searchAsync(enforceSingleEntrySearchRequest(request), handler).then( |
| | | new Function<Result, SearchResultEntry, LdapException>() { |
| | | @Override |
| | | public SearchResultEntry apply(final Result value) throws LdapException { |
| | | return handler.getSingleEntry(); |
| | | } |
| | | }, new Function<LdapException, SearchResultEntry, LdapException>() { |
| | | @Override |
| | | public SearchResultEntry apply(final LdapException error) throws LdapException { |
| | | throw handler.filterError(error); |
| | | } |
| | | })); |
| | | } |
| | | |
| | | /** |
| | |
| | | * The default implementation is to delegate. |
| | | */ |
| | | @Override |
| | | public FutureResult<Void> abandonAsync(final AbandonRequest request) { |
| | | public LdapPromise<Void> abandonAsync(final AbandonRequest request) { |
| | | return connection.abandonAsync(request); |
| | | } |
| | | |
| | |
| | | * The default implementation is to delegate. |
| | | */ |
| | | @Override |
| | | public FutureResult<Result> addAsync(final AddRequest request) { |
| | | public LdapPromise<Result> addAsync(final AddRequest request) { |
| | | return connection.addAsync(request); |
| | | } |
| | | |
| | |
| | | * The default implementation is to delegate. |
| | | */ |
| | | @Override |
| | | public FutureResult<Result> addAsync(final AddRequest request, |
| | | public LdapPromise<Result> addAsync(final AddRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | return connection.addAsync(request, intermediateResponseHandler); |
| | | } |
| | |
| | | * The default implementation is to delegate. |
| | | */ |
| | | @Override |
| | | public FutureResult<Result> applyChangeAsync(ChangeRecord request) { |
| | | public LdapPromise<Result> applyChangeAsync(ChangeRecord request) { |
| | | return connection.applyChangeAsync(request); |
| | | } |
| | | |
| | |
| | | * The default implementation is to delegate. |
| | | */ |
| | | @Override |
| | | public FutureResult<Result> applyChangeAsync(ChangeRecord request, |
| | | public LdapPromise<Result> applyChangeAsync(ChangeRecord request, |
| | | IntermediateResponseHandler intermediateResponseHandler) { |
| | | return connection.applyChangeAsync(request, intermediateResponseHandler); |
| | | } |
| | |
| | | * The default implementation is to delegate. |
| | | */ |
| | | @Override |
| | | public FutureResult<BindResult> bindAsync(final BindRequest request) { |
| | | public LdapPromise<BindResult> bindAsync(final BindRequest request) { |
| | | return connection.bindAsync(request); |
| | | } |
| | | |
| | |
| | | * The default implementation is to delegate. |
| | | */ |
| | | @Override |
| | | public FutureResult<BindResult> bindAsync(BindRequest request, |
| | | public LdapPromise<BindResult> bindAsync(BindRequest request, |
| | | IntermediateResponseHandler intermediateResponseHandler) { |
| | | return connection.bindAsync(request, intermediateResponseHandler); |
| | | } |
| | |
| | | * The default implementation is to delegate. |
| | | */ |
| | | @Override |
| | | public FutureResult<CompareResult> compareAsync(final CompareRequest request) { |
| | | public LdapPromise<CompareResult> compareAsync(final CompareRequest request) { |
| | | return connection.compareAsync(request); |
| | | } |
| | | |
| | |
| | | * The default implementation is to delegate. |
| | | */ |
| | | @Override |
| | | public FutureResult<CompareResult> compareAsync(CompareRequest request, |
| | | public LdapPromise<CompareResult> compareAsync(CompareRequest request, |
| | | IntermediateResponseHandler intermediateResponseHandler) { |
| | | return connection.compareAsync(request, intermediateResponseHandler); |
| | | } |
| | |
| | | * The default implementation is to delegate. |
| | | */ |
| | | @Override |
| | | public FutureResult<Result> deleteAsync(final DeleteRequest request) { |
| | | public LdapPromise<Result> deleteAsync(final DeleteRequest request) { |
| | | return connection.deleteAsync(request); |
| | | } |
| | | |
| | |
| | | * The default implementation is to delegate. |
| | | */ |
| | | @Override |
| | | public FutureResult<Result> deleteAsync(DeleteRequest request, |
| | | public LdapPromise<Result> deleteAsync(DeleteRequest request, |
| | | IntermediateResponseHandler intermediateResponseHandler) { |
| | | return connection.deleteAsync(request, intermediateResponseHandler); |
| | | } |
| | |
| | | * The default implementation is to delegate. |
| | | */ |
| | | @Override |
| | | public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(final ExtendedRequest<R> request) { |
| | | public <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(final ExtendedRequest<R> request) { |
| | | return connection.extendedRequestAsync(request); |
| | | } |
| | | |
| | |
| | | * The default implementation is to delegate. |
| | | */ |
| | | @Override |
| | | public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(ExtendedRequest<R> request, |
| | | public <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(ExtendedRequest<R> request, |
| | | IntermediateResponseHandler intermediateResponseHandler) { |
| | | return connection.extendedRequestAsync(request, intermediateResponseHandler); |
| | | } |
| | |
| | | * The default implementation is to delegate. |
| | | */ |
| | | @Override |
| | | public FutureResult<Result> modifyAsync(final ModifyRequest request) { |
| | | public LdapPromise<Result> modifyAsync(final ModifyRequest request) { |
| | | return connection.modifyAsync(request); |
| | | } |
| | | |
| | |
| | | * The default implementation is to delegate. |
| | | */ |
| | | @Override |
| | | public FutureResult<Result> modifyAsync(ModifyRequest request, |
| | | public LdapPromise<Result> modifyAsync(ModifyRequest request, |
| | | IntermediateResponseHandler intermediateResponseHandler) { |
| | | return connection.modifyAsync(request, intermediateResponseHandler); |
| | | } |
| | |
| | | * The default implementation is to delegate. |
| | | */ |
| | | @Override |
| | | public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request) { |
| | | public LdapPromise<Result> modifyDNAsync(final ModifyDNRequest request) { |
| | | return connection.modifyDNAsync(request); |
| | | } |
| | | |
| | |
| | | * The default implementation is to delegate. |
| | | */ |
| | | @Override |
| | | public FutureResult<Result> modifyDNAsync(ModifyDNRequest request, |
| | | public LdapPromise<Result> modifyDNAsync(ModifyDNRequest request, |
| | | IntermediateResponseHandler intermediateResponseHandler) { |
| | | return modifyDNAsync(request, intermediateResponseHandler); |
| | | } |
| | |
| | | * The default implementation is to delegate. |
| | | */ |
| | | @Override |
| | | public FutureResult<SearchResultEntry> readEntryAsync(final DN name, |
| | | public LdapPromise<SearchResultEntry> readEntryAsync(final DN name, |
| | | final Collection<String> attributeDescriptions) { |
| | | return connection.readEntryAsync(name, attributeDescriptions); |
| | | } |
| | |
| | | * The default implementation is to delegate. |
| | | */ |
| | | @Override |
| | | public FutureResult<Result> searchAsync(final SearchRequest request, |
| | | public LdapPromise<Result> searchAsync(final SearchRequest request, |
| | | final SearchResultHandler resultHandler) { |
| | | return connection.searchAsync(request, resultHandler); |
| | | } |
| | |
| | | * The default implementation is to delegate. |
| | | */ |
| | | @Override |
| | | public FutureResult<Result> searchAsync(SearchRequest request, |
| | | public LdapPromise<Result> searchAsync(SearchRequest request, |
| | | IntermediateResponseHandler intermediateResponseHandler, SearchResultHandler entryHandler) { |
| | | return connection.searchAsync(request, intermediateResponseHandler, entryHandler); |
| | | } |
| | |
| | | * The default implementation is to delegate. |
| | | */ |
| | | @Override |
| | | public FutureResult<SearchResultEntry> searchSingleEntryAsync(final SearchRequest request) { |
| | | public LdapPromise<SearchResultEntry> searchSingleEntryAsync(final SearchRequest request) { |
| | | return connection.searchSingleEntryAsync(request); |
| | | } |
| | | |
| | |
| | | |
| | | @Override |
| | | public void close() { |
| | | // Should we cancel the future? |
| | | // Should we cancel the promise? |
| | | factory.close(); |
| | | } |
| | | |
| | |
| | | * policy here such as waiting indefinitely, or for a configurable |
| | | * timeout period. |
| | | */ |
| | | throw newErrorResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR, |
| | | throw newLdapException(ResultCode.CLIENT_SIDE_CONNECT_ERROR, |
| | | "No operational connection factories available", lastFailure); |
| | | } |
| | | } |
| | |
| | | import org.forgerock.opendj.ldap.responses.ExtendedResult; |
| | | import org.forgerock.opendj.ldap.responses.Result; |
| | | |
| | | import static org.forgerock.opendj.ldap.FutureResultWrapper.*; |
| | | import static org.forgerock.opendj.ldap.spi.LdapPromises.*; |
| | | |
| | | /** |
| | | * An abstract connection whose asynchronous methods are implemented in terms of |
| | |
| | | * synchronous connections. |
| | | */ |
| | | @Override |
| | | public FutureResult<Void> abandonAsync(final AbandonRequest request) { |
| | | public LdapPromise<Void> abandonAsync(final AbandonRequest request) { |
| | | throw new UnsupportedOperationException("Abandon requests are not supported for synchronous connections"); |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> addAsync(final AddRequest request, |
| | | public LdapPromise<Result> addAsync(final AddRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | try { |
| | | return onSuccess(add(request)); |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<BindResult> bindAsync(final BindRequest request, |
| | | public LdapPromise<BindResult> bindAsync(final BindRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | try { |
| | | return onSuccess(bind(request)); |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<CompareResult> compareAsync(final CompareRequest request, |
| | | public LdapPromise<CompareResult> compareAsync(final CompareRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | try { |
| | | return onSuccess(compare(request)); |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> deleteAsync(final DeleteRequest request, |
| | | public LdapPromise<Result> deleteAsync(final DeleteRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | try { |
| | | return onSuccess(delete(request)); |
| | |
| | | } |
| | | |
| | | @Override |
| | | public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(final ExtendedRequest<R> request, |
| | | public <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(final ExtendedRequest<R> request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | try { |
| | | return onSuccess(extendedRequest(request, intermediateResponseHandler)); |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> modifyAsync(final ModifyRequest request, |
| | | public LdapPromise<Result> modifyAsync(final ModifyRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | try { |
| | | return onSuccess(modify(request)); |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request, |
| | | public LdapPromise<Result> modifyDNAsync(final ModifyDNRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | try { |
| | | return onSuccess(modifyDN(request)); |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> searchAsync(final SearchRequest request, |
| | | public LdapPromise<Result> searchAsync(final SearchRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler, final SearchResultHandler entryHandler) { |
| | | try { |
| | | return onSuccess(search(request, entryHandler)); |
| | |
| | | } |
| | | } |
| | | |
| | | private <R extends Result> FutureResult<R> onFailure(final LdapException e) { |
| | | return newFailedFutureResult(e); |
| | | private <R extends Result> LdapPromise<R> onFailure(final LdapException e) { |
| | | return newFailedLdapPromise(e); |
| | | } |
| | | |
| | | private <R extends Result> FutureResult<R> onSuccess(final R result) { |
| | | return newSuccessfulFutureResult(result); |
| | | private <R extends Result> LdapPromise<R> onSuccess(final R result) { |
| | | return newSuccessfulLdapPromise(result); |
| | | } |
| | | |
| | | } |
| | |
| | | * Bind operations are not supported by pre-authenticated connections. |
| | | * These methods will always throw {@code UnsupportedOperationException}. |
| | | */ |
| | | public FutureResult<BindResult> bindAsync(final BindRequest request, |
| | | public LdapPromise<BindResult> bindAsync(final BindRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler, |
| | | final ResultHandler<? super BindResult> resultHandler) { |
| | | throw new UnsupportedOperationException(); |
| | |
| | | import org.forgerock.util.Reject; |
| | | import org.forgerock.util.promise.FailureHandler; |
| | | import org.forgerock.util.promise.Promise; |
| | | import org.forgerock.util.promise.PromiseImpl; |
| | | import org.forgerock.util.promise.SuccessHandler; |
| | | |
| | | import com.forgerock.opendj.util.ReferenceCountedObject; |
| | |
| | | currentPoolSize(), maxPoolSize, error)); |
| | | |
| | | /* |
| | | * There may be many pending futures waiting for a connection |
| | | * There may be many pending promises waiting for a connection |
| | | * attempt to succeed. In some situations the number of pending |
| | | * futures may exceed the pool size and the number of outstanding |
| | | * connection attempts. If only one pending future is resolved per |
| | | * failed connection attempt then some pending futures will be left |
| | | * promises may exceed the pool size and the number of outstanding |
| | | * connection attempts. If only one pending promises is resolved per |
| | | * failed connection attempt then some pending promises will be left |
| | | * unresolved. Therefore, a failed connection attempt must fail all |
| | | * pending futures, even if some of the subsequent connection |
| | | * pending promises, even if some of the subsequent connection |
| | | * attempts succeed, which is unlikely (if one fails, then they are |
| | | * all likely to fail). |
| | | */ |
| | | final List<QueueElement> waitingFutures = |
| | | new LinkedList<CachedConnectionPool.QueueElement>(); |
| | | final List<QueueElement> waitingPromises = new LinkedList<CachedConnectionPool.QueueElement>(); |
| | | synchronized (queue) { |
| | | while (hasWaitingFutures()) { |
| | | waitingFutures.add(queue.removeFirst()); |
| | | while (hasWaitingPromises()) { |
| | | waitingPromises.add(queue.removeFirst()); |
| | | } |
| | | } |
| | | for (QueueElement waitingFuture : waitingFutures) { |
| | | waitingFuture.getWaitingFuture().handleError(error); |
| | | for (QueueElement waitingPromise : waitingPromises) { |
| | | waitingPromise.getWaitingPromise().handleError(error); |
| | | } |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Void> abandonAsync(final AbandonRequest request) { |
| | | public LdapPromise<Void> abandonAsync(final AbandonRequest request) { |
| | | return checkState().abandonAsync(request); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> addAsync(AddRequest request) { |
| | | public LdapPromise<Result> addAsync(AddRequest request) { |
| | | return addAsync(request, null); |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> addAsync(final AddRequest request, |
| | | public LdapPromise<Result> addAsync(final AddRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | return checkState().addAsync(request, intermediateResponseHandler); |
| | | } |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> applyChangeAsync(final ChangeRecord request) { |
| | | public LdapPromise<Result> applyChangeAsync(final ChangeRecord request) { |
| | | return checkState().applyChangeAsync(request, null); |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> applyChangeAsync(final ChangeRecord request, |
| | | public LdapPromise<Result> applyChangeAsync(final ChangeRecord request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | return checkState().applyChangeAsync(request, intermediateResponseHandler); |
| | | } |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<BindResult> bindAsync(BindRequest request) { |
| | | public LdapPromise<BindResult> bindAsync(BindRequest request) { |
| | | return bindAsync(request, null); |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<BindResult> bindAsync(final BindRequest request, |
| | | public LdapPromise<BindResult> bindAsync(final BindRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | return checkState().bindAsync(request, intermediateResponseHandler); |
| | | } |
| | |
| | | /* |
| | | * The connection may have been disconnected by the remote |
| | | * server, but the server may still be available. In order to |
| | | * avoid leaving pending futures hanging indefinitely, we should |
| | | * avoid leaving pending promises hanging indefinitely, we should |
| | | * try to reconnect immediately. No need to release/acquire |
| | | * availableConnections. |
| | | */ |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<CompareResult> compareAsync(CompareRequest request) { |
| | | public LdapPromise<CompareResult> compareAsync(CompareRequest request) { |
| | | return compareAsync(request, null); |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<CompareResult> compareAsync(final CompareRequest request, |
| | | public LdapPromise<CompareResult> compareAsync(final CompareRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | return checkState().compareAsync(request, intermediateResponseHandler); |
| | | } |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> deleteAsync(DeleteRequest request) { |
| | | public LdapPromise<Result> deleteAsync(DeleteRequest request) { |
| | | return deleteAsync(request, null); |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> deleteAsync(final DeleteRequest request, |
| | | public LdapPromise<Result> deleteAsync(final DeleteRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | return checkState().deleteAsync(request, intermediateResponseHandler); |
| | | } |
| | |
| | | } |
| | | |
| | | @Override |
| | | public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(ExtendedRequest<R> request) { |
| | | public <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(ExtendedRequest<R> request) { |
| | | return extendedRequestAsync(request, null); |
| | | } |
| | | |
| | | @Override |
| | | public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(final ExtendedRequest<R> request, |
| | | public <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(final ExtendedRequest<R> request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | return checkState().extendedRequestAsync(request, intermediateResponseHandler); |
| | | } |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> modifyAsync(ModifyRequest request) { |
| | | public LdapPromise<Result> modifyAsync(ModifyRequest request) { |
| | | return modifyAsync(request, null); |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> modifyAsync(final ModifyRequest request, |
| | | public LdapPromise<Result> modifyAsync(final ModifyRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | return checkState().modifyAsync(request, intermediateResponseHandler); |
| | | } |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> modifyDNAsync(ModifyDNRequest request) { |
| | | public LdapPromise<Result> modifyDNAsync(ModifyDNRequest request) { |
| | | return modifyDNAsync(request, null); |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request, |
| | | public LdapPromise<Result> modifyDNAsync(final ModifyDNRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | return checkState().modifyDNAsync(request, intermediateResponseHandler); |
| | | } |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<SearchResultEntry> readEntryAsync(final DN name, |
| | | public LdapPromise<SearchResultEntry> readEntryAsync(final DN name, |
| | | final Collection<String> attributeDescriptions) { |
| | | return checkState().readEntryAsync(name, attributeDescriptions); |
| | | } |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> searchAsync(SearchRequest request, SearchResultHandler resultHandler) { |
| | | public LdapPromise<Result> searchAsync(SearchRequest request, SearchResultHandler resultHandler) { |
| | | return searchAsync(request, null, resultHandler); |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> searchAsync(final SearchRequest request, |
| | | public LdapPromise<Result> searchAsync(final SearchRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler, final SearchResultHandler entryHandler) { |
| | | return checkState().searchAsync(request, intermediateResponseHandler, entryHandler); |
| | | } |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<SearchResultEntry> searchSingleEntryAsync(final SearchRequest request) { |
| | | public LdapPromise<SearchResultEntry> searchSingleEntryAsync(final SearchRequest request) { |
| | | return checkState().searchSingleEntryAsync(request); |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | private boolean isTimedOutQueuedConnection(final QueueElement holder, |
| | | final long timeoutMillis) { |
| | | return holder != null && !holder.isWaitingFuture() && holder.hasTimedOut(timeoutMillis); |
| | | private boolean isTimedOutQueuedConnection(final QueueElement holder, final long timeoutMillis) { |
| | | return holder != null && !holder.isWaitingPromise() && holder.hasTimedOut(timeoutMillis); |
| | | } |
| | | } |
| | | |
| | |
| | | @Override |
| | | protected void finalize() throws Throwable { |
| | | if (!isClosed()) { |
| | | logIfDebugEnabled("CONNECTION POOL: connection leaked! It was allocated here: ", |
| | | stackTrace); |
| | | logIfDebugEnabled("CONNECTION POOL: connection leaked! It was allocated here: ", stackTrace); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * A queue element is either a pending connection request future awaiting an |
| | | * A queue element is either a pending connection request promise awaiting an |
| | | * {@code Connection} or it is an unused {@code Connection} awaiting a |
| | | * connection request. |
| | | */ |
| | |
| | | this.stack = null; |
| | | } |
| | | |
| | | QueueElement(final long timestampMillis, |
| | | final StackTraceElement[] stack) { |
| | | this.value = new FutureResultImpl<Connection>(); |
| | | QueueElement(final long timestampMillis, final StackTraceElement[] stack) { |
| | | this.value = PromiseImpl.create(); |
| | | this.timestampMillis = timestampMillis; |
| | | this.stack = stack; |
| | | } |
| | |
| | | } |
| | | |
| | | @SuppressWarnings("unchecked") |
| | | FutureResultImpl<Connection> getWaitingFuture() { |
| | | return (FutureResultImpl<Connection>) value; |
| | | PromiseImpl<Connection, LdapException> getWaitingPromise() { |
| | | return (PromiseImpl<Connection, LdapException>) value; |
| | | } |
| | | |
| | | boolean hasTimedOut(final long timeLimitMillis) { |
| | | return timestampMillis < timeLimitMillis; |
| | | } |
| | | |
| | | boolean isWaitingFuture() { |
| | | return value instanceof FutureResultImpl; |
| | | boolean isWaitingPromise() { |
| | | return value instanceof PromiseImpl; |
| | | } |
| | | } |
| | | |
| | |
| | | try { |
| | | return getConnectionAsync().getOrThrow(); |
| | | } catch (final InterruptedException e) { |
| | | throw newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED, e); |
| | | throw newLdapException(ResultCode.CLIENT_SIDE_USER_CANCELLED, e); |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | if (holder.isWaitingFuture()) { |
| | | if (holder.isWaitingPromise()) { |
| | | // Grow the pool if needed. |
| | | final FutureResult<Connection> future = holder.getWaitingFuture(); |
| | | if (!future.isDone() && availableConnections.tryAcquire()) { |
| | | final Promise<Connection, LdapException> promise = holder.getWaitingPromise(); |
| | | if (!promise.isDone() && availableConnections.tryAcquire()) { |
| | | pendingConnectionAttempts.incrementAndGet(); |
| | | factory.getConnectionAsync().onSuccess(connectionSuccessHandler) |
| | | .onFailure(connectionFailureHandler); |
| | | } |
| | | return future; |
| | | return promise; |
| | | } |
| | | |
| | | // There was a completed connection attempt. |
| | |
| | | int blocked = 0; |
| | | synchronized (queue) { |
| | | for (QueueElement qe : queue) { |
| | | if (qe.isWaitingFuture()) { |
| | | if (qe.isWaitingPromise()) { |
| | | blocked++; |
| | | } else { |
| | | in++; |
| | |
| | | } |
| | | |
| | | private boolean hasWaitingConnections() { |
| | | return !queue.isEmpty() && !queue.getFirst().isWaitingFuture(); |
| | | return !queue.isEmpty() && !queue.getFirst().isWaitingPromise(); |
| | | } |
| | | |
| | | private boolean hasWaitingFutures() { |
| | | return !queue.isEmpty() && queue.getFirst().isWaitingFuture(); |
| | | private boolean hasWaitingPromises() { |
| | | return !queue.isEmpty() && queue.getFirst().isWaitingPromise(); |
| | | } |
| | | |
| | | private void publishConnection(final Connection connection) { |
| | |
| | | boolean connectionPoolIsClosing = false; |
| | | |
| | | synchronized (queue) { |
| | | if (hasWaitingFutures()) { |
| | | if (hasWaitingPromises()) { |
| | | connectionPoolIsClosing = isClosed; |
| | | holder = queue.removeFirst(); |
| | | } else if (isClosed) { |
| | |
| | | } |
| | | } |
| | | |
| | | // There was waiting future, so complete it. |
| | | // There was waiting promise, so complete it. |
| | | if (connectionPoolIsClosing) { |
| | | // The connection will be closed, so decrease the pool size. |
| | | availableConnections.release(); |
| | |
| | | |
| | | if (holder != null) { |
| | | final LdapException e = |
| | | LdapException.newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED, |
| | | newLdapException(ResultCode.CLIENT_SIDE_USER_CANCELLED, |
| | | ERR_CONNECTION_POOL_CLOSING.get(toString()).toString()); |
| | | holder.getWaitingFuture().handleError(e); |
| | | holder.getWaitingPromise().handleError(e); |
| | | |
| | | logger.debug(LocalizableMessage.raw( |
| | | "Connection attempt failed: availableConnections=%d, maxPoolSize=%d", |
| | | currentPoolSize(), maxPoolSize, e)); |
| | | } |
| | | } else { |
| | | holder.getWaitingFuture().handleResult( |
| | | holder.getWaitingPromise().handleResult( |
| | | newPooledConnection(connection, holder.getStackTrace())); |
| | | } |
| | | } |
| | |
| | | * <h4>Performing operations asynchronously</h4> |
| | | * <p> |
| | | * Asynchronous methods, identified by their {@code Async} suffix, are |
| | | * non-blocking, returning a {@link FutureResult} or sub-type thereof which can |
| | | * be used for retrieving the result using the {@link FutureResult#get} method. |
| | | * Operation failures, for whatever reason, are signalled by the |
| | | * {@link FutureResult#get()} method throwing an {@link LdapException}. |
| | | * non-blocking, returning a {@link LdapPromise} or sub-type thereof which can |
| | | * be used for retrieving the result using the {@link LdapPromise#get} method. |
| | | * Operation failures, for whatever reason, are signaled by the |
| | | * {@link LdapPromise#get()} method throwing an {@link LdapException}. |
| | | * <p> |
| | | * In addition to returning a {@code FutureResult}, all asynchronous methods |
| | | * In addition to returning a {@link LdapPromise}, all asynchronous methods |
| | | * accept a {@link ResultHandler} which will be notified upon completion of the |
| | | * operation. |
| | | * <p> |
| | |
| | | * Connection connection2 = ...; |
| | | * AddRequest request = ...; |
| | | * // Add the entry to the first server (don't block). |
| | | * FutureResult future1 = connection1.add(request); |
| | | * LdapPromise promise1 = connection1.add(request); |
| | | * // Add the entry to the second server (in parallel). |
| | | * FutureResult future2 = connection2.add(request); |
| | | * LdapPromise promise2 = connection2.add(request); |
| | | * // Total time = is O(1) instead of O(n). |
| | | * future1.get(); |
| | | * future2.get(); |
| | | * promise1.get(); |
| | | * promise2.get(); |
| | | * </pre> |
| | | * |
| | | * More complex client applications can take advantage of a fully asynchronous |
| | |
| | | * request. |
| | | * <p> |
| | | * Abandon requests do not have a response, so invoking the method get() on |
| | | * the returned future will not block, nor return anything (it is Void), but |
| | | * the returned promise will not block, nor return anything (it is Void), but |
| | | * may throw an exception if a problem occurred while sending the abandon |
| | | * request. In addition the returned future may be used in order to |
| | | * request. In addition the returned promise may be used in order to |
| | | * determine the message ID of the abandon request. |
| | | * <p> |
| | | * <b>Note:</b> a more convenient approach to abandoning unfinished |
| | | * asynchronous operations is provided via the |
| | | * {@link FutureResult#cancel(boolean)} method. |
| | | * {@link LdapPromise#cancel(boolean)} method. |
| | | * |
| | | * @param request |
| | | * The request identifying the operation to be abandoned. |
| | | * @return A future whose result is Void. |
| | | * @return A promise whose result is Void. |
| | | * @throws UnsupportedOperationException |
| | | * If this connection does not support abandon operations. |
| | | * @throws IllegalStateException |
| | |
| | | * @throws NullPointerException |
| | | * If {@code request} was {@code null}. |
| | | */ |
| | | FutureResult<Void> abandonAsync(AbandonRequest request); |
| | | LdapPromise<Void> abandonAsync(AbandonRequest request); |
| | | |
| | | /** |
| | | * Adds an entry to the Directory Server using the provided add request. |
| | |
| | | * |
| | | * @param request |
| | | * The add request. |
| | | * @return A future representing the result of the operation. |
| | | * @return A promise representing the result of the operation. |
| | | * @throws UnsupportedOperationException |
| | | * If this connection does not support add operations. |
| | | * @throws IllegalStateException |
| | |
| | | * @throws NullPointerException |
| | | * If {@code request} was {@code null}. |
| | | */ |
| | | FutureResult<Result> addAsync(AddRequest request); |
| | | LdapPromise<Result> addAsync(AddRequest request); |
| | | |
| | | /** |
| | | * Asynchronously adds an entry to the Directory Server using the provided |
| | |
| | | * An intermediate response handler which can be used to process |
| | | * any intermediate responses as they are received, may be |
| | | * {@code null}. |
| | | * @return A future representing the result of the operation. |
| | | * @return A promise representing the result of the operation. |
| | | * @throws UnsupportedOperationException |
| | | * If this connection does not support add operations. |
| | | * @throws IllegalStateException |
| | |
| | | * @throws NullPointerException |
| | | * If {@code request} was {@code null}. |
| | | */ |
| | | FutureResult<Result> addAsync(AddRequest request, IntermediateResponseHandler intermediateResponseHandler); |
| | | LdapPromise<Result> addAsync(AddRequest request, IntermediateResponseHandler intermediateResponseHandler); |
| | | |
| | | /** |
| | | * Registers the provided connection event listener so that it will be |
| | |
| | | * |
| | | * @param request |
| | | * The change request. |
| | | * @return A future representing the result of the operation. |
| | | * @return A promise representing the result of the operation. |
| | | * @throws UnsupportedOperationException |
| | | * If this connection does not support the provided change |
| | | * request. |
| | |
| | | * @throws NullPointerException |
| | | * If {@code request} was {@code null}. |
| | | */ |
| | | FutureResult<Result> applyChangeAsync(ChangeRecord request); |
| | | LdapPromise<Result> applyChangeAsync(ChangeRecord request); |
| | | |
| | | /** |
| | | * Asynchronously applies the provided change request to the Directory |
| | |
| | | * An intermediate response handler which can be used to process |
| | | * any intermediate responses as they are received, may be |
| | | * {@code null}. |
| | | * @return A future representing the result of the operation. |
| | | * @return A promise representing the result of the operation. |
| | | * @throws UnsupportedOperationException |
| | | * If this connection does not support the provided change |
| | | * request. |
| | |
| | | * @throws NullPointerException |
| | | * If {@code request} was {@code null}. |
| | | */ |
| | | FutureResult<Result> applyChangeAsync(ChangeRecord request, |
| | | LdapPromise<Result> applyChangeAsync(ChangeRecord request, |
| | | IntermediateResponseHandler intermediateResponseHandler); |
| | | |
| | | /** |
| | |
| | | * |
| | | * @param request |
| | | * The bind request. |
| | | * @return A future representing the result of the operation. |
| | | * @return A promise representing the result of the operation. |
| | | * @throws UnsupportedOperationException |
| | | * If this connection does not support bind operations. |
| | | * @throws IllegalStateException |
| | |
| | | * @throws NullPointerException |
| | | * If {@code request} was {@code null}. |
| | | */ |
| | | FutureResult<BindResult> bindAsync(BindRequest request); |
| | | LdapPromise<BindResult> bindAsync(BindRequest request); |
| | | |
| | | /** |
| | | * Asynchronously authenticates to the Directory Server using the provided |
| | |
| | | * An intermediate response handler which can be used to process |
| | | * any intermediate responses as they are received, may be |
| | | * {@code null}. |
| | | * @return A future representing the result of the operation. |
| | | * @return A promise representing the result of the operation. |
| | | * @throws UnsupportedOperationException |
| | | * If this connection does not support bind operations. |
| | | * @throws IllegalStateException |
| | |
| | | * @throws NullPointerException |
| | | * If {@code request} was {@code null}. |
| | | */ |
| | | FutureResult<BindResult> bindAsync(BindRequest request, IntermediateResponseHandler intermediateResponseHandler); |
| | | LdapPromise<BindResult> bindAsync(BindRequest request, IntermediateResponseHandler intermediateResponseHandler); |
| | | |
| | | /** |
| | | * Releases any resources associated with this connection. For physical |
| | |
| | | * |
| | | * @param request |
| | | * The compare request. |
| | | * @return A future representing the result of the operation. |
| | | * @return A promise representing the result of the operation. |
| | | * @throws UnsupportedOperationException |
| | | * If this connection does not support compare operations. |
| | | * @throws IllegalStateException |
| | |
| | | * @throws NullPointerException |
| | | * If {@code request} was {@code null}. |
| | | */ |
| | | FutureResult<CompareResult> compareAsync(CompareRequest request); |
| | | LdapPromise<CompareResult> compareAsync(CompareRequest request); |
| | | |
| | | /** |
| | | * Asynchronously compares an entry in the Directory Server using the |
| | |
| | | * An intermediate response handler which can be used to process |
| | | * any intermediate responses as they are received, may be |
| | | * {@code null}. |
| | | * @return A future representing the result of the operation. |
| | | * @return A promise representing the result of the operation. |
| | | * @throws UnsupportedOperationException |
| | | * If this connection does not support compare operations. |
| | | * @throws IllegalStateException |
| | |
| | | * @throws NullPointerException |
| | | * If {@code request} was {@code null}. |
| | | */ |
| | | FutureResult<CompareResult> compareAsync(CompareRequest request, |
| | | LdapPromise<CompareResult> compareAsync(CompareRequest request, |
| | | IntermediateResponseHandler intermediateResponseHandler); |
| | | |
| | | /** |
| | |
| | | * |
| | | * @param request |
| | | * The delete request. |
| | | * @return A future representing the result of the operation. |
| | | * @return A promise representing the result of the operation. |
| | | * @throws UnsupportedOperationException |
| | | * If this connection does not support delete operations. |
| | | * @throws IllegalStateException |
| | |
| | | * @throws NullPointerException |
| | | * If {@code request} was {@code null}. |
| | | */ |
| | | FutureResult<Result> deleteAsync(DeleteRequest request); |
| | | LdapPromise<Result> deleteAsync(DeleteRequest request); |
| | | |
| | | /** |
| | | * Asynchronously deletes an entry from the Directory Server using the |
| | |
| | | * An intermediate response handler which can be used to process |
| | | * any intermediate responses as they are received, may be |
| | | * {@code null}. |
| | | * @return A future representing the result of the operation. |
| | | * @return A promise representing the result of the operation. |
| | | * @throws UnsupportedOperationException |
| | | * If this connection does not support delete operations. |
| | | * @throws IllegalStateException |
| | |
| | | * @throws NullPointerException |
| | | * If {@code request} was {@code null}. |
| | | */ |
| | | FutureResult<Result> deleteAsync(DeleteRequest request, IntermediateResponseHandler intermediateResponseHandler); |
| | | LdapPromise<Result> deleteAsync(DeleteRequest request, IntermediateResponseHandler intermediateResponseHandler); |
| | | |
| | | /** |
| | | * Requests that the Directory Server performs the provided extended |
| | |
| | | * The type of result returned by the extended request. |
| | | * @param request |
| | | * The extended request. |
| | | * @return A future representing the result of the operation. |
| | | * @return A promise representing the result of the operation. |
| | | * @throws UnsupportedOperationException |
| | | * If this connection does not support extended operations. |
| | | * @throws IllegalStateException |
| | |
| | | * @throws NullPointerException |
| | | * If {@code request} was {@code null}. |
| | | */ |
| | | <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(ExtendedRequest<R> request); |
| | | <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(ExtendedRequest<R> request); |
| | | |
| | | /** |
| | | * Asynchronously performs the provided extended request in the Directory |
| | |
| | | * An intermediate response handler which can be used to process |
| | | * any intermediate responses as they are received, may be |
| | | * {@code null}. |
| | | * @return A future representing the result of the operation. |
| | | * @return A promise representing the result of the operation. |
| | | * @throws UnsupportedOperationException |
| | | * If this connection does not support extended operations. |
| | | * @throws IllegalStateException |
| | |
| | | * @throws NullPointerException |
| | | * If {@code request} was {@code null}. |
| | | */ |
| | | <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(ExtendedRequest<R> request, |
| | | <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(ExtendedRequest<R> request, |
| | | IntermediateResponseHandler intermediateResponseHandler); |
| | | |
| | | /** |
| | |
| | | * |
| | | * @param request |
| | | * The modify request. |
| | | * @return A future representing the result of the operation. |
| | | * @return A promise representing the result of the operation. |
| | | * @throws UnsupportedOperationException |
| | | * If this connection does not support modify operations. |
| | | * @throws IllegalStateException |
| | |
| | | * @throws NullPointerException |
| | | * If {@code request} was {@code null}. |
| | | */ |
| | | FutureResult<Result> modifyAsync(ModifyRequest request); |
| | | LdapPromise<Result> modifyAsync(ModifyRequest request); |
| | | |
| | | /** |
| | | * Asynchronously modifies an entry in the Directory Server using the |
| | |
| | | * An intermediate response handler which can be used to process |
| | | * any intermediate responses as they are received, may be |
| | | * {@code null}. |
| | | * @return A future representing the result of the operation. |
| | | * @return A promise representing the result of the operation. |
| | | * @throws UnsupportedOperationException |
| | | * If this connection does not support modify operations. |
| | | * @throws IllegalStateException |
| | |
| | | * @throws NullPointerException |
| | | * If {@code request} was {@code null}. |
| | | */ |
| | | FutureResult<Result> modifyAsync(ModifyRequest request, IntermediateResponseHandler intermediateResponseHandler); |
| | | LdapPromise<Result> modifyAsync(ModifyRequest request, IntermediateResponseHandler intermediateResponseHandler); |
| | | |
| | | /** |
| | | * Renames an entry in the Directory Server using the provided modify DN |
| | |
| | | * |
| | | * @param request |
| | | * The modify DN request. |
| | | * @return A future representing the result of the operation. |
| | | * @return A promise representing the result of the operation. |
| | | * @throws UnsupportedOperationException |
| | | * If this connection does not support modify DN operations. |
| | | * @throws IllegalStateException |
| | |
| | | * @throws NullPointerException |
| | | * If {@code request} was {@code null}. |
| | | */ |
| | | FutureResult<Result> modifyDNAsync(ModifyDNRequest request); |
| | | LdapPromise<Result> modifyDNAsync(ModifyDNRequest request); |
| | | |
| | | /** |
| | | * Asynchronously renames an entry in the Directory Server using the |
| | |
| | | * An intermediate response handler which can be used to process |
| | | * any intermediate responses as they are received, may be |
| | | * {@code null}. |
| | | * @return A future representing the result of the operation. |
| | | * @return A promise representing the result of the operation. |
| | | * @throws UnsupportedOperationException |
| | | * If this connection does not support modify DN operations. |
| | | * @throws IllegalStateException |
| | |
| | | * @throws NullPointerException |
| | | * If {@code request} was {@code null}. |
| | | */ |
| | | FutureResult<Result> modifyDNAsync(ModifyDNRequest request, |
| | | LdapPromise<Result> modifyDNAsync(ModifyDNRequest request, |
| | | IntermediateResponseHandler intermediateResponseHandler); |
| | | |
| | | /** |
| | |
| | | * <p> |
| | | * If the requested entry is not returned by the Directory Server then the |
| | | * request will fail with an {@link EntryNotFoundException}. More |
| | | * specifically, the returned future will never return {@code null}. |
| | | * specifically, the returned promise will never return {@code null}. |
| | | * <p> |
| | | * This method is equivalent to the following code: |
| | | * |
| | |
| | | * The names of the attributes to be included with the entry, |
| | | * which may be {@code null} or empty indicating that all user |
| | | * attributes should be returned. |
| | | * @return A future representing the result of the operation. |
| | | * @return A promise representing the result of the operation. |
| | | * @throws UnsupportedOperationException |
| | | * If this connection does not support search operations. |
| | | * @throws IllegalStateException |
| | |
| | | * @throws NullPointerException |
| | | * If the {@code name} was {@code null}. |
| | | */ |
| | | FutureResult<SearchResultEntry> readEntryAsync(DN name, Collection<String> attributeDescriptions); |
| | | LdapPromise<SearchResultEntry> readEntryAsync(DN name, Collection<String> attributeDescriptions); |
| | | |
| | | /** |
| | | * Removes the provided connection event listener from this connection so |
| | |
| | | * A search result handler which can be used to asynchronously |
| | | * process the search result entries and references as they are |
| | | * received, may be {@code null}. |
| | | * @return A future representing the result of the operation. |
| | | * @return A promise representing the result of the operation. |
| | | * @throws UnsupportedOperationException |
| | | * If this connection does not support search operations. |
| | | * @throws IllegalStateException |
| | |
| | | * @throws NullPointerException |
| | | * If {@code request} was {@code null}. |
| | | */ |
| | | FutureResult<Result> searchAsync(SearchRequest request, SearchResultHandler entryHandler); |
| | | LdapPromise<Result> searchAsync(SearchRequest request, SearchResultHandler entryHandler); |
| | | |
| | | /** |
| | | * Asynchronously searches the Directory Server using the provided search |
| | |
| | | * A search result handler which can be used to asynchronously |
| | | * process the search result entries and references as they are |
| | | * received, may be {@code null}. |
| | | * @return A future representing the result of the operation. |
| | | * @return A promise representing the result of the operation. |
| | | * @throws UnsupportedOperationException |
| | | * If this connection does not support search operations. |
| | | * @throws IllegalStateException |
| | |
| | | * @throws NullPointerException |
| | | * If {@code request} was {@code null}. |
| | | */ |
| | | FutureResult<Result> searchAsync(SearchRequest request, IntermediateResponseHandler intermediateResponseHandler, |
| | | LdapPromise<Result> searchAsync(SearchRequest request, IntermediateResponseHandler intermediateResponseHandler, |
| | | SearchResultHandler entryHandler); |
| | | |
| | | /** |
| | |
| | | * <p> |
| | | * If the requested entry is not returned by the Directory Server then the |
| | | * request will fail with an {@link EntryNotFoundException}. More |
| | | * specifically, the returned future will never return {@code null}. If |
| | | * specifically, the returned promise will never return {@code null}. If |
| | | * multiple matching entries are returned by the Directory Server then the |
| | | * request will fail with an {@link MultipleEntriesFoundException}. |
| | | * |
| | | * @param request |
| | | * The search request. |
| | | * @return A future representing the result of the operation. |
| | | * @return A promise representing the result of the operation. |
| | | * @throws UnsupportedOperationException |
| | | * If this connection does not support search operations. |
| | | * @throws IllegalStateException |
| | |
| | | * @throws NullPointerException |
| | | * If the {@code request} was {@code null}. |
| | | */ |
| | | FutureResult<SearchResultEntry> searchSingleEntryAsync(SearchRequest request); |
| | | LdapPromise<SearchResultEntry> searchSingleEntryAsync(SearchRequest request); |
| | | } |
| | |
| | | |
| | | import static com.forgerock.opendj.ldap.CoreMessages.*; |
| | | import static org.forgerock.opendj.ldap.AttributeDescription.objectClass; |
| | | import static org.forgerock.opendj.ldap.LdapException.newErrorResult; |
| | | import static org.forgerock.opendj.ldap.LdapException.newLdapException; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Collection; |
| | |
| | | // First parse the change. |
| | | final AttributeDescription deltaAd = change.getAttributeDescription(); |
| | | if (change.size() != 1) { |
| | | throw newErrorResult(ResultCode.CONSTRAINT_VIOLATION, |
| | | throw newLdapException(ResultCode.CONSTRAINT_VIOLATION, |
| | | ERR_ENTRY_INCREMENT_INVALID_VALUE_COUNT.get(deltaAd.toString()).toString()); |
| | | } |
| | | final long delta; |
| | | try { |
| | | delta = change.parse().asLong(); |
| | | } catch (final Exception e) { |
| | | throw newErrorResult(ResultCode.CONSTRAINT_VIOLATION, |
| | | throw newLdapException(ResultCode.CONSTRAINT_VIOLATION, |
| | | ERR_ENTRY_INCREMENT_CANNOT_PARSE_AS_INT.get(deltaAd.toString()).toString()); |
| | | } |
| | | |
| | | // Now apply the increment to the attribute. |
| | | final Attribute oldAttribute = entry.getAttribute(deltaAd); |
| | | if (oldAttribute == null) { |
| | | throw newErrorResult(ResultCode.NO_SUCH_ATTRIBUTE, |
| | | throw newLdapException(ResultCode.NO_SUCH_ATTRIBUTE, |
| | | ERR_ENTRY_INCREMENT_NO_SUCH_ATTRIBUTE.get(deltaAd.toString()).toString()); |
| | | } |
| | | |
| | |
| | | newAttribute.add(value + delta); |
| | | } |
| | | } catch (final Exception e) { |
| | | throw newErrorResult(ResultCode.CONSTRAINT_VIOLATION, |
| | | throw newLdapException(ResultCode.CONSTRAINT_VIOLATION, |
| | | ERR_ENTRY_INCREMENT_CANNOT_PARSE_AS_INT.get(deltaAd.toString()).toString()); |
| | | } |
| | | entry.replaceAttribute(newAttribute); |
| | |
| | | entry.addAttribute(change.getAttribute(), conflictingValues); |
| | | if (!isPermissive && !conflictingValues.isEmpty()) { |
| | | // Duplicate values. |
| | | throw newErrorResult(ResultCode.ATTRIBUTE_OR_VALUE_EXISTS, |
| | | throw newLdapException(ResultCode.ATTRIBUTE_OR_VALUE_EXISTS, |
| | | ERR_ENTRY_DUPLICATE_VALUES.get( |
| | | change.getAttribute().getAttributeDescriptionAsString()).toString()); |
| | | } |
| | |
| | | entry.removeAttribute(change.getAttribute(), conflictingValues); |
| | | if (!isPermissive && (!hasChanged || !conflictingValues.isEmpty())) { |
| | | // Missing attribute or values. |
| | | throw newErrorResult(ResultCode.NO_SUCH_ATTRIBUTE, ERR_ENTRY_NO_SUCH_VALUE.get( |
| | | throw newLdapException(ResultCode.NO_SUCH_ATTRIBUTE, ERR_ENTRY_NO_SUCH_VALUE.get( |
| | | change.getAttribute().getAttributeDescriptionAsString()).toString()); |
| | | } |
| | | } else if (modType.equals(ModificationType.REPLACE)) { |
| | |
| | | } else if (modType.equals(ModificationType.INCREMENT)) { |
| | | incrementAttribute(entry, change.getAttribute()); |
| | | } else { |
| | | throw newErrorResult(ResultCode.UNWILLING_TO_PERFORM, |
| | | throw newLdapException(ResultCode.UNWILLING_TO_PERFORM, |
| | | ERR_ENTRY_UNKNOWN_MODIFICATION_TYPE.get(String.valueOf(modType)).toString()); |
| | | } |
| | | return entry; |
| | |
| | | import java.util.concurrent.TimeoutException; |
| | | import java.util.concurrent.atomic.AtomicBoolean; |
| | | import java.util.concurrent.atomic.AtomicInteger; |
| | | import java.util.concurrent.atomic.AtomicReference; |
| | | import java.util.concurrent.locks.AbstractQueuedSynchronizer; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | |
| | | import org.forgerock.opendj.ldap.responses.SearchResultEntry; |
| | | import org.forgerock.opendj.ldap.responses.SearchResultReference; |
| | | import org.forgerock.opendj.ldap.spi.ConnectionState; |
| | | import org.forgerock.opendj.ldap.spi.LdapPromiseImpl; |
| | | import org.forgerock.util.Reject; |
| | | import org.forgerock.util.promise.AsyncFunction; |
| | | import org.forgerock.util.promise.FailureHandler; |
| | | import org.forgerock.util.promise.Function; |
| | | import org.forgerock.util.promise.Promise; |
| | | import org.forgerock.util.promise.PromiseImpl; |
| | | import org.forgerock.util.promise.SuccessHandler; |
| | | |
| | | import com.forgerock.opendj.util.ReferenceCountedObject; |
| | | import com.forgerock.opendj.util.TimeSource; |
| | | |
| | | import static org.forgerock.opendj.ldap.LdapException.*; |
| | | import static org.forgerock.opendj.ldap.spi.LdapPromiseImpl.*; |
| | | import static org.forgerock.opendj.ldap.spi.LdapPromises.*; |
| | | import static org.forgerock.util.Utils.*; |
| | | |
| | | import static com.forgerock.opendj.ldap.CoreMessages.*; |
| | | import static com.forgerock.opendj.util.StaticUtils.*; |
| | |
| | | * while there are pending bind or startTLS requests. |
| | | */ |
| | | final class HeartBeatConnectionFactory implements ConnectionFactory { |
| | | |
| | | /** |
| | | * This class is responsible for performing an initial heart beat once a new |
| | | * connection has been obtained and, if the heart beat succeeds, adapting it |
| | | * to a {@code ConnectionImpl} and registering it in the table of valid |
| | | * connections. |
| | | */ |
| | | private final class ConnectionFutureResultImpl implements Runnable { |
| | | private Connection connection; |
| | | private Connection heartBeatConnection; |
| | | private LdapException heartBeatError; |
| | | |
| | | /** |
| | | * Due to a potential race between the heart beat timing out and the |
| | | * heart beat completing this atomic ensures that notification only |
| | | * occurs once. |
| | | */ |
| | | private final AtomicBoolean isComplete = new AtomicBoolean(); |
| | | |
| | | private final Function<Result, Connection, LdapException> futureSearchSuccess; |
| | | private final Function<LdapException, Connection, LdapException> futureSearchError; |
| | | |
| | | private ConnectionFutureResultImpl() { |
| | | this.futureSearchSuccess = new Function<Result, Connection, LdapException>() { |
| | | |
| | | @Override |
| | | public Connection apply(Result result) throws LdapException { |
| | | if (isComplete.compareAndSet(false, true)) { |
| | | heartBeatConnection = adaptConnection(connection); |
| | | } |
| | | |
| | | return heartBeatConnection; |
| | | } |
| | | }; |
| | | |
| | | this.futureSearchError = new Function<LdapException, Connection, LdapException>() { |
| | | @Override |
| | | public Connection apply(LdapException error) throws LdapException { |
| | | manageError(error); |
| | | throw heartBeatError; |
| | | } |
| | | }; |
| | | } |
| | | |
| | | @Override |
| | | public void run() { |
| | | manageError(newHeartBeatTimeoutError()); |
| | | } |
| | | |
| | | private void manageError(LdapException error) { |
| | | if (isComplete.compareAndSet(false, true)) { |
| | | // Ensure that the connection is closed. |
| | | if (connection != null) { |
| | | connection.close(); |
| | | connection = null; |
| | | } |
| | | releaseScheduler(); |
| | | heartBeatError = adaptHeartBeatError(error); |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | | * A connection that sends heart beats and supports all operations. |
| | | */ |
| | | private final class ConnectionImpl extends AbstractAsynchronousConnection implements |
| | | ConnectionEventListener { |
| | | private final class ConnectionImpl extends AbstractAsynchronousConnection implements ConnectionEventListener { |
| | | |
| | | /** |
| | | * A result handler wrapper for operations which timestamps the |
| | | * connection for each response received. The wrapper ensures that |
| | | * completed requests are removed from the {@code pendingResults} queue, |
| | | * as well as ensuring that requests are only completed once. |
| | | */ |
| | | private abstract class AbstractWrappedResultHandler<R> implements ResultHandler<R> { |
| | | private final AtomicBoolean completed = new AtomicBoolean(); |
| | | |
| | | @Override |
| | | public void handleError(final LdapException error) { |
| | | if (tryComplete()) { |
| | | timestamp(error); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void handleResult(final R result) { |
| | | if (tryComplete()) { |
| | | timestamp(result); |
| | | } |
| | | } |
| | | |
| | | public boolean isDone() { |
| | | return completed.get(); |
| | | } |
| | | |
| | | abstract void releaseBindOrStartTLSLockIfNeeded(); |
| | | |
| | | /** |
| | | * Attempts to complete this request, returning true if successful. |
| | | * This method is synchronized in order to avoid race conditions |
| | | * with search result processing. |
| | | */ |
| | | private synchronized boolean tryComplete() { |
| | | if (pendingResults.remove(this)) { |
| | | releaseBindOrStartTLSLockIfNeeded(); |
| | | completed.set(true); |
| | | return true; |
| | | } else { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | | * Runs pending request once the shared lock becomes available (when no |
| | | * heart beat is in progress). |
| | | * |
| | | * @param <R> |
| | | * The type of result returned by the request. |
| | | */ |
| | | private abstract class DelayedFuture<R extends Result> extends FutureResultImpl<R> implements Runnable { |
| | | private volatile FutureResult<R> innerFuture = null; |
| | | |
| | | @Override |
| | | public final int getRequestID() { |
| | | return innerFuture != null ? innerFuture.getRequestID() : -1; |
| | | } |
| | | |
| | | @Override |
| | | public final void run() { |
| | | if (!isCancelled()) { |
| | | sync.lockShared(); // Will not block. |
| | | innerFuture = dispatch(); |
| | | if (isCancelled() && !innerFuture.isCancelled()) { |
| | | innerFuture.cancel(false); |
| | | private class LdapPromiseImplWrapper<R> extends LdapPromiseImpl<R> { |
| | | protected LdapPromiseImplWrapper(final LdapPromise<R> wrappedPromise) { |
| | | super(new PromiseImpl<R, LdapException>() { |
| | | @Override |
| | | protected LdapException tryCancel(boolean mayInterruptIfRunning) { |
| | | /* |
| | | * FIXME: if the inner cancel succeeds then this promise will be |
| | | * completed and we can never indicate that this cancel request |
| | | * has succeeded. |
| | | */ |
| | | wrappedPromise.cancel(mayInterruptIfRunning); |
| | | return null; |
| | | } |
| | | } |
| | | } |
| | | |
| | | protected abstract FutureResult<R> dispatch(); |
| | | |
| | | @Override |
| | | protected final LdapException tryCancel(final boolean mayInterruptIfRunning) { |
| | | if (innerFuture != null) { |
| | | innerFuture.cancel(mayInterruptIfRunning); |
| | | } |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * A result handler wrapper for bind or startTLS requests which releases |
| | | * the bind/startTLS lock on completion. |
| | | */ |
| | | private final class WrappedBindOrStartTLSResultHandler<R> extends AbstractWrappedResultHandler<R> { |
| | | @Override |
| | | void releaseBindOrStartTLSLockIfNeeded() { |
| | | releaseBindOrStartTLSLock(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * A result handler wrapper for normal requests which does not release |
| | | * the bind/startTLS lock on completion. |
| | | */ |
| | | private final class WrappedResultHandler<R> extends AbstractWrappedResultHandler<R> { |
| | | @Override |
| | | void releaseBindOrStartTLSLockIfNeeded() { |
| | | // No-op for normal operations. |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * A result handler wrapper for search operations. Ensures that search |
| | | * results are not sent once the request has been completed (see |
| | | * markComplete()). |
| | | */ |
| | | private final class WrappedSearchResultHandler extends AbstractWrappedResultHandler<Result> implements |
| | | SearchResultHandler { |
| | | private final SearchResultHandler entryHandler; |
| | | |
| | | WrappedSearchResultHandler(final SearchResultHandler handler) { |
| | | this.entryHandler = handler; |
| | | } |
| | | |
| | | @Override |
| | | public synchronized boolean handleEntry(final SearchResultEntry entry) { |
| | | if (!isDone()) { |
| | | if (entryHandler != null) { |
| | | entryHandler.handleEntry(timestamp(entry)); |
| | | } else { |
| | | timestamp(entry); |
| | | } |
| | | return true; |
| | | } else { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public synchronized boolean handleReference(final SearchResultReference reference) { |
| | | if (!isDone()) { |
| | | if (entryHandler != null) { |
| | | entryHandler.handleReference(timestamp(reference)); |
| | | } else { |
| | | timestamp(reference); |
| | | } |
| | | return true; |
| | | } else { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | void releaseBindOrStartTLSLockIfNeeded() { |
| | | // No-op for normal operations. |
| | | }, wrappedPromise.getRequestID()); |
| | | } |
| | | } |
| | | |
| | |
| | | private final Connection connection; |
| | | |
| | | /** |
| | | * Search result handler for processing heart beat responses. |
| | | */ |
| | | private final SearchResultHandler heartBeatEntryHandler = new SearchResultHandler() { |
| | | @Override |
| | | public boolean handleEntry(final SearchResultEntry entry) { |
| | | timestamp(entry); |
| | | return true; |
| | | } |
| | | @Override |
| | | public boolean handleReference(final SearchResultReference reference) { |
| | | timestamp(reference); |
| | | return true; |
| | | } |
| | | }; |
| | | |
| | | /** |
| | | * List of pending Bind or StartTLS requests which must be invoked once |
| | | * the current heart beat completes. |
| | | */ |
| | |
| | | * signalled if no heart beat is detected within the permitted timeout |
| | | * period. |
| | | */ |
| | | private final Queue<AbstractWrappedResultHandler<?>> pendingResults = |
| | | new ConcurrentLinkedQueue<AbstractWrappedResultHandler<?>>(); |
| | | private final Queue<ResultHandler<?>> pendingResults = |
| | | new ConcurrentLinkedQueue<ResultHandler<?>>(); |
| | | |
| | | /** Internal connection state. */ |
| | | private final ConnectionState state = new ConnectionState(); |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Void> abandonAsync(final AbandonRequest request) { |
| | | public LdapPromise<Void> abandonAsync(final AbandonRequest request) { |
| | | return connection.abandonAsync(request); |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> addAsync(final AddRequest request, |
| | | public LdapPromise<Result> addAsync(final AddRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | if (checkState()) { |
| | | return then(connection.addAsync(request, intermediateResponseHandler), createResultHandler()); |
| | | return timestampPromise(connection.addAsync(request, intermediateResponseHandler)); |
| | | } else { |
| | | return newConnectionErrorFuture(); |
| | | return newConnectionErrorPromise(); |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<BindResult> bindAsync(final BindRequest request, |
| | | public LdapPromise<BindResult> bindAsync(final BindRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | if (checkState()) { |
| | | if (sync.tryLockShared()) { |
| | | // Fast path |
| | | return then(connection.bindAsync(request, intermediateResponseHandler), wrapForBindOrStartTLS()); |
| | | return timestampBindOrStartTLSPromise(connection.bindAsync(request, intermediateResponseHandler)); |
| | | } else { |
| | | /* |
| | | * A heart beat must be in progress so create a runnable |
| | | * task which will be executed when the heart beat |
| | | * completes. |
| | | */ |
| | | final DelayedFuture<BindResult> future = new DelayedFuture<BindResult>() { |
| | | return enqueueBindOrStartTLSPromise(new AsyncFunction<Void, BindResult, LdapException>() { |
| | | @Override |
| | | public FutureResult<BindResult> dispatch() { |
| | | return HeartBeatConnectionFactory.this.then( |
| | | connection.bindAsync(request, intermediateResponseHandler), wrapForBindOrStartTLS()); |
| | | public Promise<BindResult, LdapException> apply(Void value) throws LdapException { |
| | | return timestampBindOrStartTLSPromise(connection.bindAsync(request, |
| | | intermediateResponseHandler)); |
| | | } |
| | | }; |
| | | /* |
| | | * Enqueue and flush if the heart beat has completed in the |
| | | * mean time. |
| | | */ |
| | | pendingBindOrStartTLSRequests.offer(future); |
| | | flushPendingBindOrStartTLSRequests(); |
| | | return future; |
| | | }); |
| | | } |
| | | } else { |
| | | return newConnectionErrorFuture(); |
| | | return newConnectionErrorPromise(); |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<CompareResult> compareAsync(final CompareRequest request, |
| | | public LdapPromise<CompareResult> compareAsync(final CompareRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | if (checkState()) { |
| | | return then(connection.compareAsync(request, intermediateResponseHandler), createResultHandler()); |
| | | return timestampPromise(connection.compareAsync(request, intermediateResponseHandler)); |
| | | } else { |
| | | return newConnectionErrorFuture(); |
| | | return newConnectionErrorPromise(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> deleteAsync(final DeleteRequest request, |
| | | public LdapPromise<Result> deleteAsync(final DeleteRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | if (checkState()) { |
| | | return then(connection.deleteAsync(request, intermediateResponseHandler), createResultHandler()); |
| | | return timestampPromise(connection.deleteAsync(request, intermediateResponseHandler)); |
| | | } else { |
| | | return newConnectionErrorFuture(); |
| | | return newConnectionErrorPromise(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(final ExtendedRequest<R> request, |
| | | public <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(final ExtendedRequest<R> request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | if (checkState()) { |
| | | if (isStartTLSRequest(request)) { |
| | | if (sync.tryLockShared()) { |
| | | // Fast path |
| | | return then(connection.extendedRequestAsync(request, intermediateResponseHandler), |
| | | wrapForBindOrStartTLS()); |
| | | return timestampBindOrStartTLSPromise(connection.extendedRequestAsync(request, |
| | | intermediateResponseHandler)); |
| | | } else { |
| | | /* |
| | | * A heart beat must be in progress so create a runnable |
| | | * task which will be executed when the heart beat |
| | | * completes. |
| | | */ |
| | | final DelayedFuture<R> future = new DelayedFuture<R>() { |
| | | return enqueueBindOrStartTLSPromise(new AsyncFunction<Void, R, LdapException>() { |
| | | @Override |
| | | public FutureResult<R> dispatch() { |
| | | return HeartBeatConnectionFactory.this.then( |
| | | connection.extendedRequestAsync(request, intermediateResponseHandler), |
| | | wrapForBindOrStartTLS()); |
| | | public Promise<R, LdapException> apply(Void value) throws LdapException { |
| | | return timestampBindOrStartTLSPromise(connection.extendedRequestAsync( |
| | | request, intermediateResponseHandler)); |
| | | } |
| | | }; |
| | | |
| | | /* |
| | | * Enqueue and flush if the heart beat has completed in |
| | | * the mean time. |
| | | */ |
| | | pendingBindOrStartTLSRequests.offer(future); |
| | | flushPendingBindOrStartTLSRequests(); |
| | | return future; |
| | | }); |
| | | } |
| | | } else { |
| | | return then(connection.extendedRequestAsync(request, intermediateResponseHandler), |
| | | createResultHandler()); |
| | | return timestampPromise(connection.extendedRequestAsync(request, intermediateResponseHandler)); |
| | | } |
| | | } else { |
| | | return newConnectionErrorFuture(); |
| | | return newConnectionErrorPromise(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void handleConnectionClosed() { |
| | | if (state.notifyConnectionClosed()) { |
| | | failPendingResults(newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED, |
| | | failPendingResults(newLdapException(ResultCode.CLIENT_SIDE_USER_CANCELLED, |
| | | HBCF_CONNECTION_CLOSED_BY_CLIENT.get())); |
| | | synchronized (validConnections) { |
| | | connection.removeConnectionEventListener(this); |
| | | validConnections.remove(this); |
| | | if (validConnections.isEmpty()) { |
| | | /* |
| | | * This is the last active connection, so stop the |
| | | * heartbeat. |
| | | */ |
| | | // This is the last active connection, so stop the heartbeat. |
| | | heartBeatFuture.cancel(false); |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> modifyAsync(final ModifyRequest request, |
| | | public LdapPromise<Result> modifyAsync(final ModifyRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | if (checkState()) { |
| | | return then(connection.modifyAsync(request, intermediateResponseHandler), createResultHandler()); |
| | | return timestampPromise(connection.modifyAsync(request, intermediateResponseHandler)); |
| | | } else { |
| | | return newConnectionErrorFuture(); |
| | | return newConnectionErrorPromise(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request, |
| | | public LdapPromise<Result> modifyDNAsync(final ModifyDNRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | if (checkState()) { |
| | | return then(connection.modifyDNAsync(request, intermediateResponseHandler), createResultHandler()); |
| | | return timestampPromise(connection.modifyDNAsync(request, intermediateResponseHandler)); |
| | | } else { |
| | | return newConnectionErrorFuture(); |
| | | return newConnectionErrorPromise(); |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> searchAsync(final SearchRequest request, |
| | | public LdapPromise<Result> searchAsync(final SearchRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler, |
| | | final SearchResultHandler searchHandler) { |
| | | if (checkState()) { |
| | | final WrappedSearchResultHandler entryHandler = wrap(searchHandler); |
| | | return then(connection.searchAsync(request, intermediateResponseHandler, entryHandler), |
| | | createResultHandler()); |
| | | final AtomicBoolean searchDone = new AtomicBoolean(); |
| | | final SearchResultHandler entryHandler = new SearchResultHandler() { |
| | | @Override |
| | | public synchronized boolean handleEntry(SearchResultEntry entry) { |
| | | if (!searchDone.get()) { |
| | | timestamp(entry); |
| | | if (searchHandler != null) { |
| | | searchHandler.handleEntry(entry); |
| | | } |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | public synchronized boolean handleReference(SearchResultReference reference) { |
| | | if (!searchDone.get()) { |
| | | timestamp(reference); |
| | | if (searchHandler != null) { |
| | | searchHandler.handleReference(reference); |
| | | } |
| | | } |
| | | return true; |
| | | } |
| | | }; |
| | | |
| | | return timestampPromise(connection.searchAsync(request, |
| | | intermediateResponseHandler, entryHandler).onSuccessOrFailure(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | searchDone.getAndSet(true); |
| | | } |
| | | })); |
| | | } else { |
| | | return newConnectionErrorFuture(); |
| | | return newConnectionErrorPromise(); |
| | | } |
| | | } |
| | | |
| | |
| | | * Peek instead of pool because notification is responsible for |
| | | * removing the element from the queue. |
| | | */ |
| | | AbstractWrappedResultHandler<?> pendingResult; |
| | | ResultHandler<?> pendingResult; |
| | | while ((pendingResult = pendingResults.peek()) != null) { |
| | | pendingResult.handleError(error); |
| | | } |
| | |
| | | private void flushPendingBindOrStartTLSRequests() { |
| | | if (!pendingBindOrStartTLSRequests.isEmpty()) { |
| | | /* |
| | | * The pending requests will acquire the shared lock, but we |
| | | * take it here anyway to ensure that pending requests do not |
| | | * get blocked. |
| | | * The pending requests will acquire the shared lock, but we take |
| | | * it here anyway to ensure that pending requests do not get blocked. |
| | | */ |
| | | if (sync.tryLockShared()) { |
| | | try { |
| | |
| | | return request.getOID().equals(StartTLSExtendedRequest.OID); |
| | | } |
| | | |
| | | private <R> FutureResult<R> newConnectionErrorFuture() { |
| | | return FutureResultWrapper.newFailedFutureResult(state.getConnectionError()); |
| | | private <R> LdapPromise<R> newConnectionErrorPromise() { |
| | | return newFailedLdapPromise(state.getConnectionError()); |
| | | } |
| | | |
| | | private void releaseBindOrStartTLSLock() { |
| | |
| | | /** |
| | | * Sends a heart beat on this connection if required to do so. |
| | | * |
| | | * @return {@code true} if a heart beat was sent, otherwise |
| | | * {@code false}. |
| | | * @return {@code true} if a heart beat was sent, otherwise {@code false}. |
| | | */ |
| | | private boolean sendHeartBeat() { |
| | | /* |
| | |
| | | */ |
| | | if (sync.tryLockExclusively()) { |
| | | try { |
| | | FutureResult<Result> future = connection.searchAsync(heartBeatRequest, heartBeatEntryHandler); |
| | | if (future != null) { |
| | | future.onSuccess(new SuccessHandler<Result>() { |
| | | @Override |
| | | public void handleResult(Result result) { |
| | | timestamp(result); |
| | | releaseHeartBeatLock(); |
| | | } |
| | | }).onFailure(new FailureHandler<LdapException>() { |
| | | @Override |
| | | public void handleError(LdapException error) { |
| | | /* |
| | | * Connection failure will be handled by |
| | | * connection event listener. Ignore |
| | | * cancellation errors since these indicate that |
| | | * the heart beat was aborted by a client-side |
| | | * close. |
| | | */ |
| | | if (!(error instanceof CancelledResultException)) { |
| | | /* |
| | | * Log at debug level to avoid polluting the |
| | | * logs with benign password policy related |
| | | * errors. See OPENDJ-1168 and OPENDJ-1167. |
| | | */ |
| | | logger.debug(LocalizableMessage.raw("Heartbeat failed for connection factory '%s'", |
| | | factory, error)); |
| | | timestamp(error); |
| | | } |
| | | releaseHeartBeatLock(); |
| | | connection.searchAsync(heartBeatRequest, new SearchResultHandler() { |
| | | @Override |
| | | public boolean handleEntry(final SearchResultEntry entry) { |
| | | timestamp(entry); |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | public boolean handleReference(final SearchResultReference reference) { |
| | | timestamp(reference); |
| | | return true; |
| | | } |
| | | }).onSuccess(new SuccessHandler<Result>() { |
| | | @Override |
| | | public void handleResult(Result result) { |
| | | timestamp(result); |
| | | releaseHeartBeatLock(); |
| | | } |
| | | }).onFailure(new FailureHandler<LdapException>() { |
| | | @Override |
| | | public void handleError(LdapException error) { |
| | | /* |
| | | * Connection failure will be handled by connection |
| | | * event listener. Ignore cancellation errors since |
| | | * these indicate that the heart beat was aborted by |
| | | * a client-side close. |
| | | */ |
| | | if (!(error instanceof CancelledResultException)) { |
| | | /* |
| | | * Log at debug level to avoid polluting the |
| | | * logs with benign password policy related |
| | | * errors. See OPENDJ-1168 and OPENDJ-1167. |
| | | */ |
| | | logger.debug(LocalizableMessage.raw("Heartbeat failed for connection factory '%s'", |
| | | factory, error)); |
| | | timestamp(error); |
| | | } |
| | | }); |
| | | } |
| | | releaseHeartBeatLock(); |
| | | } |
| | | }); |
| | | } catch (final IllegalStateException e) { |
| | | /* |
| | | * This may happen when we attempt to send the heart beat |
| | |
| | | return response; |
| | | } |
| | | |
| | | private <R> WrappedResultHandler<R> createResultHandler() { |
| | | final WrappedResultHandler<R> h = new WrappedResultHandler<R>(); |
| | | pendingResults.add(h); |
| | | return h; |
| | | private <R extends Result> LdapPromise<R> enqueueBindOrStartTLSPromise( |
| | | AsyncFunction<Void, R, LdapException> doRequest) { |
| | | /* |
| | | * A heart beat must be in progress so create a runnable task which |
| | | * will be executed when the heart beat completes. |
| | | */ |
| | | final LdapPromiseImpl<Void> promise = newLdapPromiseImpl(); |
| | | final LdapPromise<R> result = promise.thenAsync(doRequest); |
| | | |
| | | // Enqueue and flush if the heart beat has completed in the mean |
| | | // time. |
| | | pendingBindOrStartTLSRequests.offer(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | // FIXME: Handle cancel chaining. |
| | | if (!result.isCancelled()) { |
| | | sync.lockShared(); // Will not block. |
| | | promise.handleResult((Void) null); |
| | | } |
| | | } |
| | | }); |
| | | flushPendingBindOrStartTLSRequests(); |
| | | return result; |
| | | } |
| | | |
| | | private WrappedSearchResultHandler wrap(final SearchResultHandler handler) { |
| | | final WrappedSearchResultHandler h = new WrappedSearchResultHandler(handler); |
| | | pendingResults.add(h); |
| | | return h; |
| | | private <R extends Result> LdapPromise<R> timestampPromise(LdapPromise<R> wrappedPromise) { |
| | | final LdapPromiseImpl<R> outerPromise = new LdapPromiseImplWrapper<R>(wrappedPromise); |
| | | pendingResults.add(outerPromise); |
| | | wrappedPromise.onSuccess(new SuccessHandler<R>() { |
| | | @Override |
| | | public void handleResult(R result) { |
| | | outerPromise.handleResult(result); |
| | | timestamp(result); |
| | | } |
| | | }).onFailure(new FailureHandler<LdapException>() { |
| | | @Override |
| | | public void handleError(LdapException error) { |
| | | outerPromise.handleError(error); |
| | | timestamp(error); |
| | | } |
| | | }); |
| | | outerPromise.onSuccessOrFailure(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | pendingResults.remove(outerPromise); |
| | | } |
| | | }); |
| | | if (!checkState()) { |
| | | outerPromise.handleError(state.getConnectionError()); |
| | | } |
| | | return outerPromise; |
| | | } |
| | | |
| | | private <R> WrappedBindOrStartTLSResultHandler<R> wrapForBindOrStartTLS() { |
| | | final WrappedBindOrStartTLSResultHandler<R> h = new WrappedBindOrStartTLSResultHandler<R>(); |
| | | pendingResults.add(h); |
| | | return h; |
| | | private <R extends Result> LdapPromise<R> timestampBindOrStartTLSPromise(LdapPromise<R> wrappedPromise) { |
| | | return timestampPromise(wrappedPromise).onSuccessOrFailure(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | releaseBindOrStartTLSLock(); |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | |
| | |
| | | * server is responding. |
| | | */ |
| | | acquireScheduler(); // Protect scheduler. |
| | | boolean succeeded = false; |
| | | Connection connection = null; |
| | | try { |
| | | final Connection connection = factory.getConnection(); |
| | | try { |
| | | connection.searchAsync(heartBeatRequest, null).getOrThrow(timeoutMS, |
| | | TimeUnit.MILLISECONDS); |
| | | succeeded = true; |
| | | return adaptConnection(connection); |
| | | } catch (final Exception e) { |
| | | throw adaptHeartBeatError(e); |
| | | } finally { |
| | | if (!succeeded) { |
| | | connection.close(); |
| | | } |
| | | } |
| | | } finally { |
| | | if (!succeeded) { |
| | | releaseScheduler(); |
| | | } |
| | | connection = factory.getConnection(); |
| | | connection.searchAsync(heartBeatRequest, null).getOrThrow(timeoutMS, TimeUnit.MILLISECONDS); |
| | | return registerConnection(new ConnectionImpl(connection)); |
| | | } catch (final Exception e) { |
| | | closeSilently(connection); |
| | | releaseScheduler(); |
| | | throw adaptHeartBeatError(e); |
| | | } |
| | | } |
| | | |
| | |
| | | public Promise<Connection, LdapException> getConnectionAsync() { |
| | | acquireScheduler(); // Protect scheduler. |
| | | |
| | | // Create a future responsible for chaining the initial heartbeat |
| | | // search. |
| | | final ConnectionFutureResultImpl compositeFuture = new ConnectionFutureResultImpl(); |
| | | final AtomicReference<Connection> connectionHolder = new AtomicReference<Connection>(); |
| | | final PromiseImpl<Connection, LdapException> promise = PromiseImpl.create(); |
| | | |
| | | // Request a connection and return the future representing the |
| | | // heartbeat. |
| | | return factory.getConnectionAsync().thenAsync(new AsyncFunction<Connection, Result, LdapException>() { |
| | | // Request a connection and return the promise representing the heartbeat. |
| | | factory.getConnectionAsync().thenAsync(new AsyncFunction<Connection, Result, LdapException>() { |
| | | @Override |
| | | public Promise<Result, LdapException> apply(final Connection connectionResult) { |
| | | public Promise<Result, LdapException> apply(final Connection connection) { |
| | | // Save the connection for later once the heart beat completes. |
| | | compositeFuture.connection = connectionResult; |
| | | scheduler.get().schedule(compositeFuture, timeoutMS, TimeUnit.MILLISECONDS); |
| | | return connectionResult.searchAsync(heartBeatRequest, null); |
| | | connectionHolder.set(connection); |
| | | scheduler.get().schedule(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | if (promise.tryHandleError(newHeartBeatTimeoutError())) { |
| | | closeSilently(connectionHolder.get()); |
| | | releaseScheduler(); |
| | | } |
| | | } |
| | | }, timeoutMS, TimeUnit.MILLISECONDS); |
| | | return connection.searchAsync(heartBeatRequest, null); |
| | | } |
| | | }).then(compositeFuture.futureSearchSuccess, compositeFuture.futureSearchError); |
| | | }).onSuccess(new SuccessHandler<Result>() { |
| | | @Override |
| | | public void handleResult(Result result) { |
| | | final Connection connection = connectionHolder.get(); |
| | | final ConnectionImpl connectionImpl = new ConnectionImpl(connection); |
| | | if (!promise.tryHandleResult(registerConnection(connectionImpl))) { |
| | | connectionImpl.close(); |
| | | } |
| | | } |
| | | }).onFailure(new FailureHandler<LdapException>() { |
| | | @Override |
| | | public void handleError(LdapException error) { |
| | | if (promise.tryHandleError(adaptHeartBeatError(error))) { |
| | | closeSilently(connectionHolder.get()); |
| | | releaseScheduler(); |
| | | } |
| | | } |
| | | }); |
| | | |
| | | return promise; |
| | | } |
| | | |
| | | @Override |
| | |
| | | return builder.toString(); |
| | | } |
| | | |
| | | private Connection adaptConnection(final Connection connection) { |
| | | private Connection registerConnection(final ConnectionImpl heartBeatConnection) { |
| | | synchronized (validConnections) { |
| | | final ConnectionImpl heartBeatConnection = new ConnectionImpl(connection); |
| | | if (validConnections.isEmpty()) { |
| | | /* This is the first active connection, so start the heart beat. */ |
| | | heartBeatFuture = |
| | | scheduler.get().scheduleWithFixedDelay(sendHeartBeatRunnable, 0, interval, |
| | | intervalUnit); |
| | | scheduler.get().scheduleWithFixedDelay(sendHeartBeatRunnable, 0, interval, intervalUnit); |
| | | } |
| | | validConnections.add(heartBeatConnection); |
| | | return heartBeatConnection; |
| | | } |
| | | } |
| | | |
| | | private <R extends Result> FutureResult<R> then(FutureResult<R> future, |
| | | ResultHandler<? super Object> resultHandler) { |
| | | return (FutureResult<R>) future.onSuccess(resultHandler).onFailure(resultHandler); |
| | | } |
| | | |
| | | private LdapException adaptHeartBeatError(final Exception error) { |
| | | if (error instanceof ConnectionException) { |
| | | return (LdapException) error; |
| | | } else if (error instanceof TimeoutResultException || error instanceof TimeoutException) { |
| | | return newHeartBeatTimeoutError(); |
| | | } else if (error instanceof InterruptedException) { |
| | | return newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED, error); |
| | | return newLdapException(ResultCode.CLIENT_SIDE_USER_CANCELLED, error); |
| | | } else { |
| | | return newErrorResult(ResultCode.CLIENT_SIDE_SERVER_DOWN, HBCF_HEARTBEAT_FAILED.get(), |
| | | error); |
| | | return newLdapException(ResultCode.CLIENT_SIDE_SERVER_DOWN, HBCF_HEARTBEAT_FAILED.get(), error); |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | private LdapException newHeartBeatTimeoutError() { |
| | | return newErrorResult(ResultCode.CLIENT_SIDE_SERVER_DOWN, HBCF_HEARTBEAT_TIMEOUT |
| | | return newLdapException(ResultCode.CLIENT_SIDE_SERVER_DOWN, HBCF_HEARTBEAT_TIMEOUT |
| | | .get(timeoutMS)); |
| | | } |
| | | } |
| | |
| | | import org.forgerock.opendj.ldap.responses.BindResult; |
| | | import org.forgerock.opendj.ldap.responses.CompareResult; |
| | | import org.forgerock.opendj.ldap.responses.ExtendedResult; |
| | | import org.forgerock.opendj.ldap.responses.Responses; |
| | | import org.forgerock.opendj.ldap.responses.Result; |
| | | import org.forgerock.opendj.ldap.spi.AbstractLDAPFutureResultImpl; |
| | | import org.forgerock.opendj.ldap.spi.LDAPCompareFutureResultImpl; |
| | | import org.forgerock.opendj.ldap.spi.LDAPExtendedFutureResultImpl; |
| | | import org.forgerock.opendj.ldap.spi.LDAPFutureResultImpl; |
| | | import org.forgerock.opendj.ldap.spi.LDAPSearchFutureResultImpl; |
| | | import org.forgerock.opendj.ldap.spi.BindResultLdapPromiseImpl; |
| | | import org.forgerock.opendj.ldap.spi.ExtendedResultLdapPromiseImpl; |
| | | import org.forgerock.opendj.ldap.spi.ResultLdapPromiseImpl; |
| | | import org.forgerock.opendj.ldap.spi.SearchResultLdapPromiseImpl; |
| | | import org.forgerock.util.Reject; |
| | | |
| | | import static org.forgerock.opendj.ldap.spi.LdapPromises.*; |
| | | |
| | | /** |
| | | * This class defines a pseudo-connection object that can be used for performing |
| | | * internal operations directly against a {@code ServerConnection} |
| | | * implementation. |
| | | */ |
| | | final class InternalConnection extends AbstractAsynchronousConnection { |
| | | private static final class InternalBindFutureResultImpl extends |
| | | AbstractLDAPFutureResultImpl<BindResult> { |
| | | private final BindRequest bindRequest; |
| | | |
| | | InternalBindFutureResultImpl(final int messageID, final BindRequest bindRequest, |
| | | final IntermediateResponseHandler intermediateResponseHandler, |
| | | final Connection connection) { |
| | | super(messageID, intermediateResponseHandler, connection); |
| | | this.bindRequest = bindRequest; |
| | | } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | final StringBuilder sb = new StringBuilder(); |
| | | sb.append("InternalBindFutureResultImpl("); |
| | | sb.append("bindRequest = "); |
| | | sb.append(bindRequest); |
| | | super.toString(sb); |
| | | sb.append(")"); |
| | | return sb.toString(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | protected BindResult newErrorResult(final ResultCode resultCode, final String diagnosticMessage, |
| | | final Throwable cause) { |
| | | return Responses.newBindResult(resultCode).setDiagnosticMessage(diagnosticMessage) |
| | | .setCause(cause); |
| | | } |
| | | } |
| | | |
| | | private final ServerConnection<Integer> serverConnection; |
| | | private final List<ConnectionEventListener> listeners = |
| | | new CopyOnWriteArrayList<ConnectionEventListener>(); |
| | | private final List<ConnectionEventListener> listeners = new CopyOnWriteArrayList<ConnectionEventListener>(); |
| | | private final AtomicInteger messageID = new AtomicInteger(); |
| | | |
| | | /** |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public FutureResult<Void> abandonAsync(final AbandonRequest request) { |
| | | public LdapPromise<Void> abandonAsync(final AbandonRequest request) { |
| | | final int i = messageID.getAndIncrement(); |
| | | serverConnection.handleAbandon(i, request); |
| | | return FutureResultWrapper.newSuccessfulFutureResult((Void) null, i); |
| | | return newSuccessfulLdapPromise((Void) null, i); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public FutureResult<Result> addAsync(final AddRequest request, |
| | | public LdapPromise<Result> addAsync(final AddRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | final int i = messageID.getAndIncrement(); |
| | | final LDAPFutureResultImpl future = new LDAPFutureResultImpl(i, request, intermediateResponseHandler, this); |
| | | serverConnection.handleAdd(i, request, future, future); |
| | | return future; |
| | | final ResultLdapPromiseImpl<AddRequest, Result> promise = |
| | | newResultLdapPromise(i, request, intermediateResponseHandler, this); |
| | | serverConnection.handleAdd(i, request, promise, promise); |
| | | return promise; |
| | | } |
| | | |
| | | /** |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public FutureResult<BindResult> bindAsync(final BindRequest request, |
| | | public LdapPromise<BindResult> bindAsync(final BindRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | final int i = messageID.getAndIncrement(); |
| | | final InternalBindFutureResultImpl future = new InternalBindFutureResultImpl(i, request, |
| | | intermediateResponseHandler, this); |
| | | serverConnection.handleBind(i, 3, request, future, future); |
| | | return future; |
| | | final BindResultLdapPromiseImpl promise = |
| | | newBindLdapPromise(i, request, null, intermediateResponseHandler, this); |
| | | serverConnection.handleBind(i, 3, request, promise, promise); |
| | | return promise; |
| | | } |
| | | |
| | | /** |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public FutureResult<CompareResult> compareAsync(final CompareRequest request, |
| | | public LdapPromise<CompareResult> compareAsync(final CompareRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | final int i = messageID.getAndIncrement(); |
| | | final LDAPCompareFutureResultImpl future = new LDAPCompareFutureResultImpl(i, request, |
| | | intermediateResponseHandler, this); |
| | | serverConnection.handleCompare(i, request, future, future); |
| | | return future; |
| | | final ResultLdapPromiseImpl<CompareRequest, CompareResult> promise = |
| | | newCompareLdapPromise(i, request, intermediateResponseHandler, this); |
| | | serverConnection.handleCompare(i, request, promise, promise); |
| | | return promise; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public FutureResult<Result> deleteAsync(final DeleteRequest request, |
| | | public LdapPromise<Result> deleteAsync(final DeleteRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | final int i = messageID.getAndIncrement(); |
| | | final LDAPFutureResultImpl future = new LDAPFutureResultImpl(i, request, intermediateResponseHandler, this); |
| | | serverConnection.handleDelete(i, request, future, future); |
| | | return future; |
| | | final ResultLdapPromiseImpl<DeleteRequest, Result> promise = |
| | | newResultLdapPromise(i, request, intermediateResponseHandler, this); |
| | | serverConnection.handleDelete(i, request, promise, promise); |
| | | return promise; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(final ExtendedRequest<R> request, |
| | | public <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(final ExtendedRequest<R> request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | final int i = messageID.getAndIncrement(); |
| | | final LDAPExtendedFutureResultImpl<R> future = new LDAPExtendedFutureResultImpl<R>(i, request, |
| | | intermediateResponseHandler, this); |
| | | serverConnection.handleExtendedRequest(i, request, future, future); |
| | | return future; |
| | | final ExtendedResultLdapPromiseImpl<R> promise = newExtendedLdapPromise(i, request, intermediateResponseHandler, |
| | | this); |
| | | serverConnection.handleExtendedRequest(i, request, promise, promise); |
| | | return promise; |
| | | } |
| | | |
| | | /** |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public FutureResult<Result> modifyAsync(final ModifyRequest request, |
| | | public LdapPromise<Result> modifyAsync(final ModifyRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | final int i = messageID.getAndIncrement(); |
| | | final LDAPFutureResultImpl future = new LDAPFutureResultImpl(i, request, intermediateResponseHandler, this); |
| | | serverConnection.handleModify(i, request, future, future); |
| | | return future; |
| | | final ResultLdapPromiseImpl<ModifyRequest, Result> promise = |
| | | newResultLdapPromise(i, request, intermediateResponseHandler, this); |
| | | serverConnection.handleModify(i, request, promise, promise); |
| | | return promise; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request, |
| | | public LdapPromise<Result> modifyDNAsync(final ModifyDNRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | final int i = messageID.getAndIncrement(); |
| | | final LDAPFutureResultImpl future = new LDAPFutureResultImpl(i, request, intermediateResponseHandler, this); |
| | | serverConnection.handleModifyDN(i, request, future, future); |
| | | return future; |
| | | final ResultLdapPromiseImpl<ModifyDNRequest, Result> promise = |
| | | newResultLdapPromise(i, request, intermediateResponseHandler, this); |
| | | serverConnection.handleModifyDN(i, request, promise, promise); |
| | | return promise; |
| | | } |
| | | |
| | | /** |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public FutureResult<Result> searchAsync(final SearchRequest request, |
| | | public LdapPromise<Result> searchAsync(final SearchRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler, final SearchResultHandler entryHandler) { |
| | | final int i = messageID.getAndIncrement(); |
| | | final LDAPSearchFutureResultImpl future = new LDAPSearchFutureResultImpl(i, request, entryHandler, |
| | | intermediateResponseHandler, this); |
| | | serverConnection.handleSearch(i, request, future, future, future); |
| | | return future; |
| | | final SearchResultLdapPromiseImpl promise = |
| | | newSearchLdapPromise(i, request, entryHandler, intermediateResponseHandler, this); |
| | | serverConnection.handleSearch(i, request, promise, promise, promise); |
| | | return promise; |
| | | } |
| | | |
| | | /** |
| | |
| | | |
| | | import org.forgerock.util.promise.Promise; |
| | | |
| | | import static org.forgerock.opendj.ldap.FutureResultWrapper.*; |
| | | import static org.forgerock.opendj.ldap.spi.LdapPromises.*; |
| | | import static org.forgerock.util.promise.Promises.*; |
| | | |
| | | |
| | |
| | | try { |
| | | serverConnection = factory.handleAccept(clientContext); |
| | | } catch (final LdapException e) { |
| | | return newFailedFutureResult(e); |
| | | return newFailedLdapPromise(e); |
| | | } |
| | | |
| | | return newSuccessfulPromise((Connection) new InternalConnection(serverConnection)); |
| | |
| | | public class LdapException extends IOException { |
| | | |
| | | /** |
| | | * Creates a new error result exception with the provided result code and an |
| | | * Creates a new LDAP exception with the provided result code and an |
| | | * empty diagnostic message. |
| | | * |
| | | * @param resultCode |
| | | * The result code. |
| | | * @return The new error result exception. |
| | | * @return The new LDAP exception. |
| | | * @throws IllegalArgumentException |
| | | * If the provided result code does not represent a failure. |
| | | * @throws NullPointerException |
| | | * If {@code resultCode} was {@code null}. |
| | | */ |
| | | public static LdapException newErrorResult(ResultCode resultCode) { |
| | | return newErrorResult(resultCode, null, null); |
| | | public static LdapException newLdapException(ResultCode resultCode) { |
| | | return newLdapException(resultCode, null, null); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new error result exception with the provided result code and |
| | | * Creates a new LDAP exception with the provided result code and |
| | | * diagnostic message. |
| | | * |
| | | * @param resultCode |
| | |
| | | * @param diagnosticMessage |
| | | * The diagnostic message, which may be empty or {@code null} |
| | | * indicating that none was provided. |
| | | * @return The new error result exception. |
| | | * @return The new LDAP exception. |
| | | * @throws IllegalArgumentException |
| | | * If the provided result code does not represent a failure. |
| | | * @throws NullPointerException |
| | | * If {@code resultCode} was {@code null}. |
| | | */ |
| | | public static LdapException newErrorResult(ResultCode resultCode, |
| | | public static LdapException newLdapException(ResultCode resultCode, |
| | | CharSequence diagnosticMessage) { |
| | | return newErrorResult(resultCode, diagnosticMessage, null); |
| | | return newLdapException(resultCode, diagnosticMessage, null); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new error result exception with the provided result code and |
| | | * Creates a new LDAP exception with the provided result code and |
| | | * cause. The diagnostic message will be taken from the cause, if provided. |
| | | * |
| | | * @param resultCode |
| | |
| | | * @param cause |
| | | * The throwable cause, which may be {@code null} indicating that |
| | | * none was provided. |
| | | * @return The new error result exception. |
| | | * @return The new LDAP exception. |
| | | * @throws IllegalArgumentException |
| | | * If the provided result code does not represent a failure. |
| | | * @throws NullPointerException |
| | | * If {@code resultCode} was {@code null}. |
| | | */ |
| | | public static LdapException newErrorResult(ResultCode resultCode, Throwable cause) { |
| | | return newErrorResult(resultCode, null, cause); |
| | | public static LdapException newLdapException(ResultCode resultCode, Throwable cause) { |
| | | return newLdapException(resultCode, null, cause); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new error result exception with the provided result code, |
| | | * Creates a new LDAP exception with the provided result code, |
| | | * diagnostic message, and cause. |
| | | * |
| | | * @param resultCode |
| | |
| | | * @param cause |
| | | * The throwable cause, which may be {@code null} indicating that |
| | | * none was provided. |
| | | * @return The new error result exception. |
| | | * @return The new LDAP exception. |
| | | * @throws IllegalArgumentException |
| | | * If the provided result code does not represent a failure. |
| | | * @throws NullPointerException |
| | | * If {@code resultCode} was {@code null}. |
| | | */ |
| | | public static LdapException newErrorResult(ResultCode resultCode, |
| | | public static LdapException newLdapException(ResultCode resultCode, |
| | | CharSequence diagnosticMessage, Throwable cause) { |
| | | final Result result = Responses.newResult(resultCode); |
| | | if (diagnosticMessage != null) { |
| | |
| | | result.setDiagnosticMessage(cause.getLocalizedMessage()); |
| | | } |
| | | result.setCause(cause); |
| | | return newErrorResult(result); |
| | | return newLdapException(result); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new error result exception using the provided result. |
| | | * Creates a new LDAP exception using the provided result. |
| | | * |
| | | * @param result |
| | | * The result whose result code indicates a failure. |
| | | * @return The error result exception wrapping the provided result. |
| | | * @return The LDAP exception wrapping the provided result. |
| | | * @throws IllegalArgumentException |
| | | * If the provided result does not represent a failure. |
| | | * @throws NullPointerException |
| | | * If {@code result} was {@code null}. |
| | | */ |
| | | public static LdapException newErrorResult(final Result result) { |
| | | public static LdapException newLdapException(final Result result) { |
| | | if (!result.getResultCode().isExceptional()) { |
| | | throw new IllegalArgumentException("Attempted to wrap a successful result: " + result); |
| | | } |
| | |
| | | private final Result result; |
| | | |
| | | /** |
| | | * Creates a new error result exception using the provided result. |
| | | * Creates a new LDAP exception using the provided result. |
| | | * |
| | | * @param result |
| | | * The error result. |
| New file |
| | |
| | | /* |
| | | * 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 legal-notices/CDDLv1_0.txt |
| | | * or http://forgerock.org/license/CDDLv1.0.html. |
| | | * 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 legal-notices/CDDLv1_0.txt. |
| | | * 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. |
| | | * Portions copyright 2014 ForgeRock AS |
| | | */ |
| | | |
| | | package org.forgerock.opendj.ldap; |
| | | |
| | | import org.forgerock.util.promise.AsyncFunction; |
| | | import org.forgerock.util.promise.FailureHandler; |
| | | import org.forgerock.util.promise.Function; |
| | | import org.forgerock.util.promise.Promise; |
| | | import org.forgerock.util.promise.SuccessHandler; |
| | | |
| | | /** |
| | | * A handle which can be used to retrieve the Result of an asynchronous Request. |
| | | * |
| | | * @param <S> |
| | | * The type of result returned by this promise. |
| | | */ |
| | | public interface LdapPromise<S> extends Promise<S, LdapException> { |
| | | /** |
| | | * Returns the request ID of the request if appropriate. |
| | | * |
| | | * @return The request ID, or {@code -1} if there is no request ID. |
| | | */ |
| | | int getRequestID(); |
| | | |
| | | @Override |
| | | LdapPromise<S> onSuccess(SuccessHandler<? super S> onSuccess); |
| | | |
| | | @Override |
| | | LdapPromise<S> onFailure(FailureHandler<? super LdapException> onFailure); |
| | | |
| | | @Override |
| | | LdapPromise<S> onSuccessOrFailure(Runnable onSuccessOrFailure); |
| | | |
| | | @Override |
| | | // @Checkstyle:ignore |
| | | <VOUT> LdapPromise<VOUT> then(Function<? super S, VOUT, LdapException> onSuccess); |
| | | |
| | | @Override |
| | | LdapPromise<S> then(SuccessHandler<? super S> onSuccess); |
| | | |
| | | @Override |
| | | LdapPromise<S> then(SuccessHandler<? super S> onSuccess, FailureHandler<? super LdapException> onFailure); |
| | | |
| | | @Override |
| | | LdapPromise<S> thenAlways(Runnable onSuccessOrFailure); |
| | | |
| | | @Override |
| | | // @Checkstyle:ignore |
| | | <VOUT> LdapPromise<VOUT> thenAsync(AsyncFunction<? super S, VOUT, LdapException> onSuccess); |
| | | } |
| | |
| | | |
| | | import static org.forgerock.opendj.ldap.Attributes.singletonAttribute; |
| | | import static org.forgerock.opendj.ldap.Entries.modifyEntry; |
| | | import static org.forgerock.opendj.ldap.LdapException.newErrorResult; |
| | | import static org.forgerock.opendj.ldap.LdapException.newLdapException; |
| | | import static org.forgerock.opendj.ldap.responses.Responses.newBindResult; |
| | | import static org.forgerock.opendj.ldap.responses.Responses.newCompareResult; |
| | | import static org.forgerock.opendj.ldap.responses.Responses.newResult; |
| | |
| | | final DN dn = request.getName(); |
| | | final DN parent = dn.parent(); |
| | | if (entries.containsKey(dn)) { |
| | | throw newErrorResult(ResultCode.ENTRY_ALREADY_EXISTS, "The entry '" |
| | | throw newLdapException(ResultCode.ENTRY_ALREADY_EXISTS, "The entry '" |
| | | + dn.toString() + "' already exists"); |
| | | } else if (!entries.containsKey(parent)) { |
| | | noSuchObject(parent); |
| | |
| | | && request.getAuthenticationType() == BindRequest.AUTHENTICATION_TYPE_SIMPLE) { |
| | | password = ((GenericBindRequest) request).getAuthenticationValue(); |
| | | } else { |
| | | throw newErrorResult(ResultCode.PROTOCOL_ERROR, |
| | | throw newLdapException(ResultCode.PROTOCOL_ERROR, |
| | | "non-SIMPLE authentication not supported: " |
| | | + request.getAuthenticationType()); |
| | | } |
| | | entry = getRequiredEntry(null, username); |
| | | if (!entry.containsAttribute("userPassword", password)) { |
| | | throw newErrorResult(ResultCode.INVALID_CREDENTIALS, "Wrong password"); |
| | | throw newLdapException(ResultCode.INVALID_CREDENTIALS, "Wrong password"); |
| | | } |
| | | } |
| | | resultHandler.handleResult(getBindResult(request, entry, entry)); |
| | | } catch (final LocalizedIllegalArgumentException e) { |
| | | resultHandler.handleError(newErrorResult(ResultCode.PROTOCOL_ERROR, e)); |
| | | resultHandler.handleError(newLdapException(ResultCode.PROTOCOL_ERROR, e)); |
| | | } catch (final EntryNotFoundException e) { |
| | | /* |
| | | * Usually you would not include a diagnostic message, but we'll add |
| | | * one here because the memory back-end is not intended for |
| | | * production use. |
| | | */ |
| | | resultHandler.handleError(newErrorResult(ResultCode.INVALID_CREDENTIALS, |
| | | resultHandler.handleError(newLdapException(ResultCode.INVALID_CREDENTIALS, |
| | | "Unknown user")); |
| | | } catch (final LdapException e) { |
| | | resultHandler.handleError(e); |
| | |
| | | if (next == null || !next.isChildOf(dn)) { |
| | | entries.remove(dn); |
| | | } else { |
| | | throw newErrorResult(ResultCode.NOT_ALLOWED_ON_NONLEAF); |
| | | throw newLdapException(ResultCode.NOT_ALLOWED_ON_NONLEAF); |
| | | } |
| | | } |
| | | } |
| | | resultHandler.handleResult(getResult(request, entry, null)); |
| | | } catch (final DecodeException e) { |
| | | resultHandler.handleError(newErrorResult(ResultCode.PROTOCOL_ERROR, e)); |
| | | resultHandler.handleError(newLdapException(ResultCode.PROTOCOL_ERROR, e)); |
| | | } catch (final LdapException e) { |
| | | resultHandler.handleError(e); |
| | | } |
| | |
| | | final RequestContext requestContext, final ExtendedRequest<R> request, |
| | | final IntermediateResponseHandler intermediateResponseHandler, |
| | | final ResultHandler<R> resultHandler) { |
| | | resultHandler.handleError(newErrorResult(ResultCode.UNWILLING_TO_PERFORM, |
| | | resultHandler.handleError(newLdapException(ResultCode.UNWILLING_TO_PERFORM, |
| | | "Extended request operation not supported")); |
| | | } |
| | | |
| | |
| | | public void handleModifyDN(final RequestContext requestContext, final ModifyDNRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler, |
| | | final ResultHandler<Result> resultHandler) { |
| | | resultHandler.handleError(newErrorResult(ResultCode.UNWILLING_TO_PERFORM, |
| | | resultHandler.handleError(newLdapException(ResultCode.UNWILLING_TO_PERFORM, |
| | | "ModifyDN request operation not supported")); |
| | | } |
| | | |
| | |
| | | request.getSizeLimit(), scope, |
| | | request.getControl(SimplePagedResultsControl.DECODER, new DecodeOptions())); |
| | | } else { |
| | | throw newErrorResult(ResultCode.PROTOCOL_ERROR, "Search request contains an unsupported search scope"); |
| | | throw newLdapException(ResultCode.PROTOCOL_ERROR, |
| | | "Search request contains an unsupported search scope"); |
| | | } |
| | | } catch (DecodeException e) { |
| | | resultHandler.handleError(LdapException.newErrorResult(ResultCode.PROTOCOL_ERROR, e.getMessage(), e)); |
| | | resultHandler.handleError(newLdapException(ResultCode.PROTOCOL_ERROR, e.getMessage(), e)); |
| | | } catch (final LdapException e) { |
| | | resultHandler.handleError(e); |
| | | } |
| | |
| | | final Entry entry = reader.readEntry(); |
| | | final DN dn = entry.getName(); |
| | | if (!overwrite && entries.containsKey(dn)) { |
| | | throw newErrorResult(ResultCode.ENTRY_ALREADY_EXISTS, |
| | | throw newLdapException(ResultCode.ENTRY_ALREADY_EXISTS, |
| | | "Attempted to add the entry '" + dn + "' multiple times"); |
| | | } else { |
| | | entries.put(dn, entry); |
| | |
| | | |
| | | // Check size limit. |
| | | if (sizeLimit > 0 && numberOfResults >= sizeLimit) { |
| | | throw newErrorResult(newResult(ResultCode.SIZE_LIMIT_EXCEEDED)); |
| | | throw newLdapException(newResult(ResultCode.SIZE_LIMIT_EXCEEDED)); |
| | | } |
| | | |
| | | // Ignore this entry if we haven't reached the first page yet. |
| | |
| | | request.getControl(PreReadRequestControl.DECODER, decodeOptions); |
| | | if (preRead != null) { |
| | | if (preRead.isCritical() && before == null) { |
| | | throw newErrorResult(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION); |
| | | throw newLdapException(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION); |
| | | } else { |
| | | final AttributeFilter filter = |
| | | new AttributeFilter(preRead.getAttributes(), schema); |
| | |
| | | request.getControl(PostReadRequestControl.DECODER, decodeOptions); |
| | | if (postRead != null) { |
| | | if (postRead.isCritical() && after == null) { |
| | | throw newErrorResult(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION); |
| | | throw newLdapException(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION); |
| | | } else { |
| | | final AttributeFilter filter = |
| | | new AttributeFilter(postRead.getAttributes(), schema); |
| | |
| | | } |
| | | return result; |
| | | } catch (final DecodeException e) { |
| | | throw newErrorResult(ResultCode.PROTOCOL_ERROR, e); |
| | | throw newLdapException(ResultCode.PROTOCOL_ERROR, e); |
| | | } |
| | | } |
| | | |
| | |
| | | try { |
| | | control = request.getControl(AssertionRequestControl.DECODER, decodeOptions); |
| | | } catch (final DecodeException e) { |
| | | throw newErrorResult(ResultCode.PROTOCOL_ERROR, e); |
| | | throw newLdapException(ResultCode.PROTOCOL_ERROR, e); |
| | | } |
| | | if (control != null) { |
| | | final Filter filter = control.getFilter(); |
| | | final Matcher matcher = filter.matcher(schema); |
| | | if (!matcher.matches(entry).toBoolean()) { |
| | | throw newErrorResult(ResultCode.ASSERTION_FAILED, "The filter '" |
| | | throw newLdapException(ResultCode.ASSERTION_FAILED, "The filter '" |
| | | + filter.toString() + "' did not match the entry '" |
| | | + entry.getName().toString() + "'"); |
| | | } |
| | |
| | | } |
| | | |
| | | private void noSuchObject(final DN dn) throws LdapException { |
| | | throw newErrorResult(ResultCode.NO_SUCH_OBJECT, "The entry '" + dn.toString() |
| | | throw newLdapException(ResultCode.NO_SUCH_OBJECT, "The entry '" + dn.toString() |
| | | + "' does not exist"); |
| | | } |
| | | |
| | |
| | | import static com.forgerock.opendj.ldap.CoreMessages.INFO_CANCELED_BY_SERVER_DISCONNECT; |
| | | import static com.forgerock.opendj.ldap.CoreMessages.INFO_CLIENT_CONNECTION_CLOSING; |
| | | import static com.forgerock.opendj.ldap.CoreMessages.WARN_CLIENT_DUPLICATE_MESSAGE_ID; |
| | | import static org.forgerock.opendj.ldap.LdapException.newErrorResult; |
| | | import static org.forgerock.opendj.ldap.LdapException.newLdapException; |
| | | |
| | | import java.util.Iterator; |
| | | import java.util.LinkedList; |
| | |
| | | final R cancelResult = |
| | | request.getResultDecoder().newExtendedErrorResult(ResultCode.TOO_LATE, "", |
| | | ""); |
| | | resultHandler.handleError(LdapException.newErrorResult(cancelResult)); |
| | | resultHandler.handleError(newLdapException(cancelResult)); |
| | | } |
| | | } |
| | | |
| | |
| | | * Don't change state: let the handler ack the cancellation |
| | | * request. |
| | | */ |
| | | throw (CancelledResultException) newErrorResult(ResultCode.CANCELLED, |
| | | throw (CancelledResultException) newLdapException(ResultCode.CANCELLED, |
| | | cancelRequestReason.toString()); |
| | | case TOO_LATE: |
| | | /* Already too late. Nothing to do. */ |
| | |
| | | if (cancelResultHandler != null) { |
| | | final Result result = |
| | | Responses.newGenericExtendedResult(ResultCode.CANNOT_CANCEL); |
| | | cancelResultHandler.handleError(newErrorResult(result)); |
| | | cancelResultHandler.handleError(newLdapException(result)); |
| | | } |
| | | return; |
| | | } |
| | |
| | | cancelResultHandler.handleResult(result); |
| | | } else { |
| | | final Result result = Responses.newGenericExtendedResult(ResultCode.TOO_LATE); |
| | | cancelResultHandler.handleError(LdapException.newErrorResult(result)); |
| | | cancelResultHandler.handleError(newLdapException(result)); |
| | | } |
| | | } |
| | | } |
| | |
| | | new DecodeOptions()); |
| | | } catch (final DecodeException e) { |
| | | // Couldn't decode a cancel request. |
| | | resultHandler.handleError(newErrorResult(ResultCode.PROTOCOL_ERROR, e |
| | | resultHandler.handleError(newLdapException(ResultCode.PROTOCOL_ERROR, e |
| | | .getLocalizedMessage())); |
| | | return; |
| | | } |
| | |
| | | * Couldn't find the request. Invoke on context in order |
| | | * to remove pending request. |
| | | */ |
| | | requestContext.handleError(newErrorResult(ResultCode.NO_SUCH_OPERATION)); |
| | | requestContext.handleError(newLdapException(ResultCode.NO_SUCH_OPERATION)); |
| | | } |
| | | } |
| | | } else { |
| | |
| | | |
| | | if (isClosed.get()) { |
| | | final LocalizableMessage message = INFO_CLIENT_CONNECTION_CLOSING.get(); |
| | | requestContext.handleError(newErrorResult(ResultCode.UNWILLING_TO_PERFORM, |
| | | requestContext.handleError(newLdapException(ResultCode.UNWILLING_TO_PERFORM, |
| | | message.toString())); |
| | | return false; |
| | | } else if (pendingRequests.putIfAbsent(messageID, requestContext) != null) { |
| | | final LocalizableMessage message = |
| | | WARN_CLIENT_DUPLICATE_MESSAGE_ID.get(requestContext.getMessageID()); |
| | | requestContext.handleError(newErrorResult(ResultCode.PROTOCOL_ERROR, message.toString())); |
| | | requestContext.handleError(newLdapException(ResultCode.PROTOCOL_ERROR, message.toString())); |
| | | return false; |
| | | } else if (isClosed.get()) { |
| | | /* |
| | |
| | | pendingRequests.remove(messageID); |
| | | |
| | | final LocalizableMessage message = INFO_CLIENT_CONNECTION_CLOSING.get(); |
| | | requestContext.handleError(newErrorResult(ResultCode.UNWILLING_TO_PERFORM, message.toString())); |
| | | requestContext.handleError(newLdapException(ResultCode.UNWILLING_TO_PERFORM, message.toString())); |
| | | return false; |
| | | } else { |
| | | /* |
| | |
| | | * asynchronously to a remote Directory Server using an |
| | | * {@link ConnectionFactory}. The {@link #handleResult} method is invoked when |
| | | * the operation or connection attempt completes successfully. The |
| | | * {@link #handleErrorResult} method is invoked if the operation or connection |
| | | * {@link #handleError(LdapException)} method is invoked if the operation or connection |
| | | * attempt fails. |
| | | * <p> |
| | | * Implementations of these methods should complete in a timely manner so as to |
| | |
| | | import org.forgerock.opendj.ldap.requests.SearchRequest; |
| | | import org.forgerock.opendj.ldap.responses.SearchResultEntry; |
| | | import org.forgerock.opendj.ldap.schema.CoreSchema; |
| | | import org.forgerock.util.Reject; |
| | | import org.forgerock.util.promise.Function; |
| | | |
| | | import com.forgerock.opendj.util.Collections2; |
| | | |
| | | import org.forgerock.util.Reject; |
| | | import org.forgerock.util.promise.Function; |
| | | import static org.forgerock.opendj.ldap.spi.LdapPromises.*; |
| | | |
| | | /** |
| | | * The root DSE is a DSA-specific Entry (DSE) and not part of any naming context |
| | |
| | | * <p> |
| | | * If the Root DSE is not returned by the Directory Server then the request |
| | | * will fail with an {@link EntryNotFoundException}. More specifically, the |
| | | * returned future will never return {@code null}. |
| | | * returned promise will never return {@code null}. |
| | | * |
| | | * @param connection |
| | | * A connection to the Directory Server whose Root DSE is to be |
| | |
| | | * @param handler |
| | | * A result handler which can be used to asynchronously process |
| | | * the operation result when it is received, may be {@code null}. |
| | | * @return A future representing the result of the operation. |
| | | * @return A promise representing the result of the operation. |
| | | * @throws UnsupportedOperationException |
| | | * If the connection does not support search operations. |
| | | * @throws IllegalStateException |
| | |
| | | * @throws NullPointerException |
| | | * If the {@code connection} was {@code null}. |
| | | */ |
| | | public static FutureResult<RootDSE> readRootDSEAsync(final Connection connection, |
| | | public static LdapPromise<RootDSE> readRootDSEAsync(final Connection connection, |
| | | final ResultHandler<? super RootDSE> handler) { |
| | | return FutureResultWrapper.asFutureResult(connection.searchSingleEntryAsync(SEARCH_REQUEST).then( |
| | | return connection.searchSingleEntryAsync(SEARCH_REQUEST).then( |
| | | new Function<SearchResultEntry, RootDSE, LdapException>() { |
| | | @Override |
| | | public RootDSE apply(SearchResultEntry result) { |
| | | return valueOf(result); |
| | | } |
| | | })); |
| | | }); |
| | | } |
| | | |
| | | /** |
| | |
| | | package org.forgerock.opendj.ldap.requests; |
| | | |
| | | import static com.forgerock.opendj.util.StaticUtils.copyOfBytes; |
| | | import static org.forgerock.opendj.ldap.LdapException.newErrorResult; |
| | | |
| | | import static org.forgerock.opendj.ldap.LdapException.newLdapException; |
| | | import static org.forgerock.opendj.ldap.responses.Responses.*; |
| | | |
| | | import javax.security.auth.callback.NameCallback; |
| | | import javax.security.auth.callback.PasswordCallback; |
| | |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.responses.BindResult; |
| | | import org.forgerock.opendj.ldap.responses.Responses; |
| | | import org.forgerock.util.Reject; |
| | | |
| | | import com.forgerock.opendj.util.StaticUtils; |
| | |
| | | setNextSASLCredentials((ByteString) null); |
| | | } |
| | | } catch (final SaslException e) { |
| | | throw newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR, e); |
| | | throw newLdapException(ResultCode.CLIENT_SIDE_LOCAL_ERROR, e); |
| | | } |
| | | } |
| | | |
| | |
| | | } catch (final SaslException e) { |
| | | // FIXME: I18N need to have a better error message. |
| | | // FIXME: Is this the best result code? |
| | | throw LdapException.newErrorResult(Responses.newResult( |
| | | ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage( |
| | | throw newLdapException(newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage( |
| | | "An error occurred during multi-stage authentication").setCause(e)); |
| | | } |
| | | } |
| | |
| | | import static com.forgerock.opendj.ldap.CoreMessages.ERR_SASL_PROTOCOL_ERROR; |
| | | import static com.forgerock.opendj.util.StaticUtils.copyOfBytes; |
| | | import static com.forgerock.opendj.util.StaticUtils.getExceptionMessage; |
| | | import static org.forgerock.opendj.ldap.LdapException.newErrorResult; |
| | | import static org.forgerock.opendj.ldap.LdapException.newLdapException; |
| | | |
| | | import java.util.HashMap; |
| | | import java.util.LinkedHashMap; |
| | |
| | | setNextSASLCredentials((ByteString) null); |
| | | } |
| | | } catch (final SaslException e) { |
| | | throw newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR, e); |
| | | throw newLdapException(ResultCode.CLIENT_SIDE_LOCAL_ERROR, e); |
| | | } |
| | | } |
| | | |
| | |
| | | } catch (final SaslException e) { |
| | | // FIXME: I18N need to have a better error message. |
| | | // FIXME: Is this the best result code? |
| | | throw newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR, |
| | | throw newLdapException(ResultCode.CLIENT_SIDE_LOCAL_ERROR, |
| | | "An error occurred during multi-stage authentication", e); |
| | | } |
| | | } |
| | |
| | | } catch (final SaslException e) { |
| | | final LocalizableMessage msg = |
| | | ERR_SASL_PROTOCOL_ERROR.get(SASL_MECHANISM_NAME, getExceptionMessage(e)); |
| | | throw newErrorResult(ResultCode.CLIENT_SIDE_DECODING_ERROR, msg.toString(), e); |
| | | throw newLdapException(ResultCode.CLIENT_SIDE_DECODING_ERROR, msg.toString(), e); |
| | | } |
| | | } |
| | | |
| | |
| | | } catch (final SaslException e) { |
| | | final LocalizableMessage msg = |
| | | ERR_SASL_PROTOCOL_ERROR.get(SASL_MECHANISM_NAME, getExceptionMessage(e)); |
| | | throw newErrorResult(ResultCode.CLIENT_SIDE_ENCODING_ERROR, msg.toString(), e); |
| | | throw newLdapException(ResultCode.CLIENT_SIDE_ENCODING_ERROR, msg.toString(), e); |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | package org.forgerock.opendj.ldap.requests; |
| | | |
| | | import static org.forgerock.opendj.ldap.LdapException.newErrorResult; |
| | | import static org.forgerock.opendj.ldap.LdapException.newLdapException; |
| | | |
| | | import javax.security.sasl.Sasl; |
| | | import javax.security.sasl.SaslClient; |
| | |
| | | setNextSASLCredentials((ByteString) null); |
| | | } |
| | | } catch (final SaslException e) { |
| | | throw newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR, e); |
| | | throw newLdapException(ResultCode.CLIENT_SIDE_LOCAL_ERROR, e); |
| | | } |
| | | } |
| | | |
| | |
| | | } catch (final SaslException e) { |
| | | // FIXME: I18N need to have a better error message. |
| | | // FIXME: Is this the best result code? |
| | | throw LdapException.newErrorResult(Responses.newResult( |
| | | throw newLdapException(Responses.newResult( |
| | | ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage( |
| | | "An error occurred during multi-stage authentication").setCause(e)); |
| | | } |
| | |
| | | import static com.forgerock.opendj.ldap.CoreMessages.ERR_SASL_PROTOCOL_ERROR; |
| | | import static com.forgerock.opendj.util.StaticUtils.copyOfBytes; |
| | | import static com.forgerock.opendj.util.StaticUtils.getExceptionMessage; |
| | | import static org.forgerock.opendj.ldap.LdapException.newErrorResult; |
| | | import static org.forgerock.opendj.ldap.LdapException.newLdapException; |
| | | |
| | | import java.security.PrivilegedActionException; |
| | | import java.security.PrivilegedExceptionAction; |
| | |
| | | if (authenticationID == null) { |
| | | // FIXME: I18N need to have a better error message. |
| | | // FIXME: Is this the best result code? |
| | | throw LdapException.newErrorResult(Responses.newResult( |
| | | throw newLdapException(Responses.newResult( |
| | | ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage( |
| | | "No authentication ID specified for GSSAPI SASL authentication")); |
| | | } |
| | |
| | | if (password == null) { |
| | | // FIXME: I18N need to have a better error message. |
| | | // FIXME: Is this the best result code? |
| | | throw LdapException.newErrorResult(Responses.newResult( |
| | | throw newLdapException(Responses.newResult( |
| | | ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage( |
| | | "No password specified for GSSAPI SASL authentication")); |
| | | } |
| | |
| | | final LocalizableMessage message = |
| | | ERR_LDAPAUTH_GSSAPI_LOCAL_AUTHENTICATION_FAILED.get(StaticUtils |
| | | .getExceptionMessage(e)); |
| | | throw LdapException.newErrorResult(Responses.newResult( |
| | | throw newLdapException(Responses.newResult( |
| | | ResultCode.CLIENT_SIDE_LOCAL_ERROR) |
| | | .setDiagnosticMessage(message.toString()).setCause(e)); |
| | | } |
| | |
| | | } catch (final SaslException e) { |
| | | // FIXME: I18N need to have a better error message. |
| | | // FIXME: Is this the best result code? |
| | | throw LdapException.newErrorResult(Responses.newResult( |
| | | throw newLdapException(Responses.newResult( |
| | | ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage( |
| | | "An error occurred during multi-stage authentication") |
| | | .setCause(e)); |
| | |
| | | } |
| | | return saslClient; |
| | | } catch (final SaslException e) { |
| | | throw newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR, e); |
| | | throw newLdapException(ResultCode.CLIENT_SIDE_LOCAL_ERROR, e); |
| | | } |
| | | } |
| | | }); |
| | |
| | | final LocalizableMessage msg = |
| | | ERR_SASL_CONTEXT_CREATE_ERROR.get(SASL_MECHANISM_NAME, |
| | | getExceptionMessage(e)); |
| | | throw newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR, msg.toString(), e); |
| | | throw newLdapException(ResultCode.CLIENT_SIDE_LOCAL_ERROR, msg.toString(), e); |
| | | } |
| | | } |
| | | } |
| | |
| | | final LocalizableMessage msg = |
| | | ERR_SASL_PROTOCOL_ERROR |
| | | .get(SASL_MECHANISM_NAME, getExceptionMessage(e)); |
| | | throw newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR, msg.toString(), e); |
| | | throw newLdapException(ResultCode.CLIENT_SIDE_LOCAL_ERROR, msg.toString(), e); |
| | | } |
| | | } |
| | | } |
| | |
| | | } catch (final SaslException e) { |
| | | final LocalizableMessage msg = |
| | | ERR_SASL_PROTOCOL_ERROR.get(SASL_MECHANISM_NAME, getExceptionMessage(e)); |
| | | throw newErrorResult(ResultCode.CLIENT_SIDE_DECODING_ERROR, msg.toString(), e); |
| | | throw newLdapException(ResultCode.CLIENT_SIDE_DECODING_ERROR, msg.toString(), e); |
| | | } |
| | | } |
| | | |
| | |
| | | } catch (final SaslException e) { |
| | | final LocalizableMessage msg = |
| | | ERR_SASL_PROTOCOL_ERROR.get(SASL_MECHANISM_NAME, getExceptionMessage(e)); |
| | | throw newErrorResult(ResultCode.CLIENT_SIDE_ENCODING_ERROR, msg.toString(), e); |
| | | throw newLdapException(ResultCode.CLIENT_SIDE_ENCODING_ERROR, msg.toString(), e); |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | package org.forgerock.opendj.ldap.requests; |
| | | |
| | | import static org.forgerock.opendj.ldap.LdapException.newErrorResult; |
| | | import static org.forgerock.opendj.ldap.LdapException.newLdapException; |
| | | |
| | | import javax.security.auth.callback.NameCallback; |
| | | import javax.security.auth.callback.PasswordCallback; |
| | |
| | | setNextSASLCredentials((ByteString) null); |
| | | } |
| | | } catch (final SaslException e) { |
| | | throw newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR, e); |
| | | throw newLdapException(ResultCode.CLIENT_SIDE_LOCAL_ERROR, e); |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | package org.forgerock.opendj.ldap.responses; |
| | | |
| | | import static org.forgerock.opendj.ldap.LdapException.newErrorResult; |
| | | import static org.forgerock.opendj.ldap.LdapException.newLdapException; |
| | | |
| | | import org.forgerock.opendj.ldap.DecodeException; |
| | | import org.forgerock.opendj.ldap.DecodeOptions; |
| | |
| | | request.getResultDecoder().newExtendedErrorResult(result.getResultCode(), |
| | | result.getMatchedDN(), result.getDiagnosticMessage()); |
| | | adaptedResult.setCause(result.getCause()); |
| | | resultHandler.handleError(newErrorResult(adaptedResult)); |
| | | resultHandler.handleError(newLdapException(adaptedResult)); |
| | | } |
| | | |
| | | @Override |
| | |
| | | resultHandler.handleResult(adaptedResult); |
| | | } catch (final DecodeException e) { |
| | | final R adaptedResult = request.getResultDecoder().adaptDecodeException(e); |
| | | resultHandler.handleError(newErrorResult(adaptedResult)); |
| | | resultHandler.handleError(newLdapException(adaptedResult)); |
| | | } |
| | | } |
| | | |
| | |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.EntryNotFoundException; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.FutureResult; |
| | | import org.forgerock.opendj.ldap.LdapPromise; |
| | | import org.forgerock.opendj.ldap.LinkedAttribute; |
| | | import org.forgerock.opendj.ldap.RDN; |
| | | import org.forgerock.util.Reject; |
| | |
| | | import com.forgerock.opendj.util.StaticUtils; |
| | | |
| | | import static org.forgerock.opendj.ldap.AttributeDescription.*; |
| | | import static org.forgerock.opendj.ldap.FutureResultWrapper.*; |
| | | import static org.forgerock.opendj.ldap.spi.LdapPromises.*; |
| | | |
| | | import static com.forgerock.opendj.ldap.CoreMessages.*; |
| | | |
| | |
| | | * <p> |
| | | * If the requested schema is not returned by the Directory Server then the |
| | | * request will fail with an {@link EntryNotFoundException}. More |
| | | * specifically, the returned future will never return {@code null}. |
| | | * specifically, the returned promise will never return {@code null}. |
| | | * |
| | | * @param connection |
| | | * A connection to the Directory Server whose schema is to be |
| | |
| | | * @param name |
| | | * The distinguished name of the subschema sub-entry. |
| | | * the operation result when it is received, may be {@code null}. |
| | | * @return A future representing the retrieved schema. |
| | | * @return A promise representing the retrieved schema. |
| | | * @throws UnsupportedOperationException |
| | | * If the connection does not support search operations. |
| | | * @throws IllegalStateException |
| | |
| | | * @throws NullPointerException |
| | | * If the {@code connection} or {@code name} was {@code null}. |
| | | */ |
| | | public static FutureResult<Schema> readSchemaAsync(final Connection connection, final DN name) { |
| | | public static LdapPromise<Schema> readSchemaAsync(final Connection connection, final DN name) { |
| | | final SchemaBuilder builder = new SchemaBuilder(); |
| | | return asFutureResult(builder.addSchemaAsync(connection, name, true).then( |
| | | return builder.addSchemaAsync(connection, name, true).then( |
| | | new Function<SchemaBuilder, Schema, LdapException>() { |
| | | @Override |
| | | public Schema apply(SchemaBuilder builder) throws LdapException { |
| | | return builder.toSchema(); |
| | | } |
| | | })); |
| | | }); |
| | | } |
| | | |
| | | /** |
| | |
| | | * <p> |
| | | * If the requested entry or its associated schema are not returned by the |
| | | * Directory Server then the request will fail with an |
| | | * {@link EntryNotFoundException}. More specifically, the returned future |
| | | * {@link EntryNotFoundException}. More specifically, the returned promise |
| | | * will never return {@code null}. |
| | | * <p> |
| | | * This implementation first reads the {@code subschemaSubentry} attribute |
| | |
| | | * @param name |
| | | * The distinguished name of the entry whose schema is to be |
| | | * located. |
| | | * @return A future representing the retrieved schema. |
| | | * @return A promise representing the retrieved schema. |
| | | * @throws UnsupportedOperationException |
| | | * If the connection does not support search operations. |
| | | * @throws IllegalStateException |
| | |
| | | * @throws NullPointerException |
| | | * If the {@code connection} or {@code name} was {@code null}. |
| | | */ |
| | | public static FutureResult<Schema> readSchemaForEntryAsync(final Connection connection, final DN name) { |
| | | public static LdapPromise<Schema> readSchemaForEntryAsync(final Connection connection, final DN name) { |
| | | final SchemaBuilder builder = new SchemaBuilder(); |
| | | return asFutureResult(builder.addSchemaForEntryAsync(connection, name, true).then( |
| | | return builder.addSchemaForEntryAsync(connection, name, true).then( |
| | | new Function<SchemaBuilder, Schema, LdapException>() { |
| | | |
| | | @Override |
| | | public Schema apply(SchemaBuilder builder) throws LdapException { |
| | | return builder.toSchema(); |
| | | } |
| | | })); |
| | | }); |
| | | } |
| | | |
| | | /** |
| | |
| | | import org.forgerock.opendj.ldap.EntryNotFoundException; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.Filter; |
| | | import org.forgerock.opendj.ldap.FutureResult; |
| | | import org.forgerock.opendj.ldap.LdapPromise; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.SearchScope; |
| | | import org.forgerock.opendj.ldap.requests.Requests; |
| | |
| | | import com.forgerock.opendj.util.SubstringReader; |
| | | |
| | | import static org.forgerock.opendj.ldap.LdapException.*; |
| | | import static org.forgerock.opendj.ldap.FutureResultWrapper.*; |
| | | import static org.forgerock.opendj.ldap.schema.Schema.*; |
| | | import static org.forgerock.opendj.ldap.schema.SchemaConstants.*; |
| | | import static org.forgerock.opendj.ldap.schema.SchemaUtils.*; |
| | | import static org.forgerock.opendj.ldap.spi.LdapPromises.*; |
| | | |
| | | import static com.forgerock.opendj.ldap.CoreMessages.*; |
| | | import static com.forgerock.opendj.util.StaticUtils.*; |
| | |
| | | |
| | | if (subentryAttr == null || subentryAttr.isEmpty()) { |
| | | // Did not get the subschema sub-entry attribute. |
| | | throw newErrorResult(ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED, |
| | | throw newLdapException(ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED, |
| | | ERR_NO_SUBSCHEMA_SUBENTRY_ATTR.get(name.toString()).toString()); |
| | | } |
| | | |
| | |
| | | try { |
| | | subschemaDN = DN.valueOf(dnString); |
| | | } catch (final LocalizedIllegalArgumentException e) { |
| | | throw newErrorResult(ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED, |
| | | throw newLdapException(ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED, |
| | | ERR_INVALID_SUBSCHEMA_SUBENTRY_ATTR.get(name.toString(), dnString, |
| | | e.getMessageObject()).toString()); |
| | | } |
| | |
| | | * @param overwrite |
| | | * {@code true} if existing schema elements with the same |
| | | * conflicting OIDs should be overwritten. |
| | | * @return A future representing the updated schema builder. |
| | | * @return A promise representing the updated schema builder. |
| | | * @throws UnsupportedOperationException |
| | | * If the connection does not support search operations. |
| | | * @throws IllegalStateException |
| | |
| | | * @throws NullPointerException |
| | | * If the {@code connection} or {@code name} was {@code null}. |
| | | */ |
| | | public FutureResult<SchemaBuilder> addSchemaAsync(final Connection connection, final DN name, |
| | | public LdapPromise<SchemaBuilder> addSchemaAsync(final Connection connection, final DN name, |
| | | final boolean overwrite) { |
| | | // The call to addSchema will perform copyOnWrite. |
| | | final SearchRequest request = getReadSchemaSearchRequest(name); |
| | | |
| | | return asFutureResult(connection.searchSingleEntryAsync(request).then( |
| | | return connection.searchSingleEntryAsync(getReadSchemaSearchRequest(name)).then( |
| | | new Function<SearchResultEntry, SchemaBuilder, LdapException>() { |
| | | @Override |
| | | public SchemaBuilder apply(SearchResultEntry result) throws LdapException { |
| | | addSchema(result, overwrite); |
| | | return SchemaBuilder.this; |
| | | } |
| | | })); |
| | | }); |
| | | } |
| | | |
| | | /** |
| | |
| | | * <p> |
| | | * This implementation first reads the {@code subschemaSubentry} attribute |
| | | * of the entry in order to identify the schema and then invokes |
| | | * {@link #addSchemaAsync(Connection, DN, ResultHandler, boolean)} to read |
| | | * the schema. |
| | | * {@link #addSchemaAsync(Connection, DN, boolean)} to read the schema. |
| | | * |
| | | * @param connection |
| | | * A connection to the Directory Server whose schema is to be |
| | |
| | | * @param overwrite |
| | | * {@code true} if existing schema elements with the same |
| | | * conflicting OIDs should be overwritten. |
| | | * @return A future representing the updated schema builder. |
| | | * @return A promise representing the updated schema builder. |
| | | * @throws UnsupportedOperationException |
| | | * If the connection does not support search operations. |
| | | * @throws IllegalStateException |
| | |
| | | * @throws NullPointerException |
| | | * If the {@code connection} or {@code name} was {@code null}. |
| | | */ |
| | | public FutureResult<SchemaBuilder> addSchemaForEntryAsync(final Connection connection, final DN name, |
| | | final boolean overwrite) { |
| | | final SearchRequest request = getReadSchemaForEntrySearchRequest(name); |
| | | |
| | | return asFutureResult(connection.searchSingleEntryAsync(request).thenAsync( |
| | | public LdapPromise<SchemaBuilder> addSchemaForEntryAsync(final Connection connection, final DN name, |
| | | final boolean overwrite) { |
| | | return connection.searchSingleEntryAsync(getReadSchemaForEntrySearchRequest(name)).thenAsync( |
| | | new AsyncFunction<SearchResultEntry, SchemaBuilder, LdapException>() { |
| | | @Override |
| | | public Promise<SchemaBuilder, LdapException> apply(SearchResultEntry result) |
| | | throws LdapException { |
| | | public Promise<SchemaBuilder, LdapException> apply(SearchResultEntry result) throws LdapException { |
| | | final DN subschemaDN = getSubschemaSubentryDN(name, result); |
| | | return addSchemaAsync(connection, subschemaDN, overwrite); |
| | | } |
| | | })); |
| | | }); |
| | | } |
| | | |
| | | /** |
| File was renamed from opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPBindFutureResultImpl.java |
| | |
| | | |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.IntermediateResponseHandler; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.requests.BindClient; |
| | | import org.forgerock.opendj.ldap.requests.BindRequest; |
| | | import org.forgerock.opendj.ldap.responses.BindResult; |
| | | import org.forgerock.opendj.ldap.responses.Responses; |
| | | import org.forgerock.util.promise.PromiseImpl; |
| | | |
| | | /** |
| | | * Bind result future implementation. |
| | | * Bind result promise implementation. |
| | | */ |
| | | public final class LDAPBindFutureResultImpl extends AbstractLDAPFutureResultImpl<BindResult> { |
| | | public final class BindResultLdapPromiseImpl extends ResultLdapPromiseImpl<BindRequest, BindResult> { |
| | | private final BindClient bindClient; |
| | | |
| | | /** |
| | | * Creates an bind future result. |
| | | * |
| | | * @param requestID |
| | | * identifier of the request |
| | | * @param bindClient |
| | | * client that binds to the server |
| | | * @param intermediateResponseHandler |
| | | * handler that consumes intermediate responses from extended |
| | | * operations |
| | | * @param connection |
| | | * the connection to directory server |
| | | */ |
| | | public LDAPBindFutureResultImpl(final int requestID, final BindClient bindClient, |
| | | BindResultLdapPromiseImpl(final int requestID, final BindRequest request, final BindClient bindClient, |
| | | final IntermediateResponseHandler intermediateResponseHandler, |
| | | final Connection connection) { |
| | | super(requestID, intermediateResponseHandler, connection); |
| | | super(new PromiseImpl<BindResult, LdapException>() { |
| | | protected LdapException tryCancel(boolean mayInterruptIfRunning) { |
| | | /* |
| | | * No other operations can be performed while a bind is active. |
| | | * Therefore it is not possible to cancel bind or requests, |
| | | * since doing so will leave the connection in a state which |
| | | * prevents other operations from being performed. |
| | | */ |
| | | return null; |
| | | } |
| | | }, requestID, request, intermediateResponseHandler, connection); |
| | | this.bindClient = bindClient; |
| | | } |
| | | |
| | |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | final StringBuilder sb = new StringBuilder(); |
| | | sb.append("LDAPBindFutureResultImpl("); |
| | | sb.append("bindClient = "); |
| | | sb.append(bindClient); |
| | | super.toString(sb); |
| | | sb.append(")"); |
| | | return sb.toString(); |
| | | } |
| | | |
| | | /** |
| | | * Returns the client. |
| | | * Returns the bind client. |
| | | * |
| | | * @return the bind client |
| | | * @return The bind client. |
| | | */ |
| | | public BindClient getBindClient() { |
| | | return bindClient; |
| | | } |
| | | |
| | | @Override |
| | | protected BindResult newErrorResult(final ResultCode resultCode, final String diagnosticMessage, |
| | | BindResult newErrorResult(final ResultCode resultCode, final String diagnosticMessage, |
| | | final Throwable cause) { |
| | | return Responses.newBindResult(resultCode).setDiagnosticMessage(diagnosticMessage) |
| | | .setCause(cause); |
| New file |
| | |
| | | /* |
| | | * 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 legal-notices/CDDLv1_0.txt |
| | | * or http://forgerock.org/license/CDDLv1.0.html. |
| | | * 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 legal-notices/CDDLv1_0.txt. |
| | | * 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-2010 Sun Microsystems, Inc. |
| | | * Portions copyright 2011-2014 ForgeRock AS. |
| | | */ |
| | | |
| | | package org.forgerock.opendj.ldap.spi; |
| | | |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.DecodeException; |
| | | import org.forgerock.opendj.ldap.DecodeOptions; |
| | | import org.forgerock.opendj.ldap.IntermediateResponseHandler; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.requests.ExtendedRequest; |
| | | import org.forgerock.opendj.ldap.requests.Requests; |
| | | import org.forgerock.opendj.ldap.requests.StartTLSExtendedRequest; |
| | | import org.forgerock.opendj.ldap.responses.ExtendedResult; |
| | | import org.forgerock.util.promise.PromiseImpl; |
| | | |
| | | import static org.forgerock.opendj.ldap.LdapException.*; |
| | | |
| | | /** |
| | | * Extended result promise implementation. |
| | | * |
| | | * @param <S> |
| | | * The type of result returned by this promise. |
| | | */ |
| | | public final class ExtendedResultLdapPromiseImpl<S extends ExtendedResult> extends |
| | | ResultLdapPromiseImpl<ExtendedRequest<S>, S> { |
| | | ExtendedResultLdapPromiseImpl(final int requestID, final ExtendedRequest<S> request, |
| | | final IntermediateResponseHandler intermediateResponseHandler, |
| | | final Connection connection) { |
| | | super(new PromiseImpl<S, LdapException>() { |
| | | @Override |
| | | protected LdapException tryCancel(boolean mayInterruptIfRunning) { |
| | | if (!StartTLSExtendedRequest.OID.equals(request.getOID())) { |
| | | connection.abandonAsync(Requests.newAbandonRequest(requestID)); |
| | | return newLdapException(ResultCode.CLIENT_SIDE_USER_CANCELLED); |
| | | } |
| | | |
| | | /* |
| | | * No other operations can be performed while a startTLS is active. |
| | | * Therefore it is not possible to cancel startTLS requests, |
| | | * since doing so will leave the connection in a state which |
| | | * prevents other operations from being performed. |
| | | */ |
| | | return null; |
| | | } |
| | | }, requestID, request, intermediateResponseHandler, connection); |
| | | } |
| | | |
| | | @Override |
| | | public boolean isBindOrStartTLS() { |
| | | return StartTLSExtendedRequest.OID.equals(getRequest().getOID()); |
| | | } |
| | | |
| | | /** |
| | | * Decode an extended result. |
| | | * |
| | | * @param result |
| | | * Extended result to decode. |
| | | * @param options |
| | | * Decoding options. |
| | | * @return The decoded extended result. |
| | | * @throws DecodeException |
| | | * If a problem occurs during decoding. |
| | | */ |
| | | public S decodeResult(final ExtendedResult result, final DecodeOptions options) throws DecodeException { |
| | | return getRequest().getResultDecoder().decodeExtendedResult(result, options); |
| | | } |
| | | |
| | | @Override |
| | | S newErrorResult(final ResultCode resultCode, final String diagnosticMessage, |
| | | final Throwable cause) { |
| | | return getRequest().getResultDecoder().newExtendedErrorResult(resultCode, "", diagnosticMessage); |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 legal-notices/CDDLv1_0.txt |
| | | * or http://forgerock.org/license/CDDLv1.0.html. |
| | | * 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 legal-notices/CDDLv1_0.txt. |
| | | * 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 2014 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.ldap.spi; |
| | | |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LdapPromise; |
| | | import org.forgerock.opendj.ldap.ResultHandler; |
| | | import org.forgerock.util.promise.Promise; |
| | | import org.forgerock.util.promise.PromiseImpl; |
| | | import org.forgerock.util.promise.Promises; |
| | | |
| | | /** |
| | | * This class provides an implementation of the {@link LdapPromise}. |
| | | * |
| | | * @param <S> |
| | | * The type of result returned by this promise. |
| | | * @see Promise |
| | | * @see Promises |
| | | * @see LdapPromise |
| | | */ |
| | | public class LdapPromiseImpl<S> extends LdapPromiseWrapper<S, PromiseImpl<S, LdapException>> implements |
| | | LdapPromise<S>, ResultHandler<S> { |
| | | |
| | | /** |
| | | * Creates a new {@link LdapPromiseImpl} from a wrapped existing {@link PromiseImpl}. |
| | | * |
| | | * @param wrappedPromise |
| | | * The {@link Promise} to wrap. |
| | | * @param requestID |
| | | * Identifier of the request. |
| | | */ |
| | | protected LdapPromiseImpl(PromiseImpl<S, LdapException> wrappedPromise, int requestID) { |
| | | super(wrappedPromise, requestID); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new {@link LdapPromiseImpl}. |
| | | * |
| | | * @param <S> |
| | | * The type of result of the promise. |
| | | * @return a new {@link LdapPromiseImpl} |
| | | */ |
| | | public static <S> LdapPromiseImpl<S> newLdapPromiseImpl() { |
| | | return newLdapPromiseImpl(-1); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new {@link LdapPromiseImpl} with a requestID. |
| | | * |
| | | * @param <S> |
| | | * The type of result of the promise. |
| | | * @param requestID |
| | | * Identifier of the request. |
| | | * @return a new {@link LdapPromiseImpl} |
| | | */ |
| | | public static <S> LdapPromiseImpl<S> newLdapPromiseImpl(int requestID) { |
| | | return new LdapPromiseImpl<S>(PromiseImpl.<S, LdapException> create(), requestID); |
| | | } |
| | | |
| | | @Override |
| | | public void handleError(LdapException error) { |
| | | getWrappedPromise().handleError(error); |
| | | } |
| | | |
| | | @Override |
| | | public void handleResult(S result) { |
| | | getWrappedPromise().handleResult(result); |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 legal-notices/CDDLv1_0.txt |
| | | * or http://forgerock.org/license/CDDLv1.0.html. |
| | | * 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 legal-notices/CDDLv1_0.txt. |
| | | * 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 2014 ForgeRock AS. |
| | | */ |
| | | |
| | | package org.forgerock.opendj.ldap.spi; |
| | | |
| | | import java.util.concurrent.ExecutionException; |
| | | import java.util.concurrent.TimeUnit; |
| | | import java.util.concurrent.TimeoutException; |
| | | |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LdapPromise; |
| | | import org.forgerock.util.promise.AsyncFunction; |
| | | import org.forgerock.util.promise.FailureHandler; |
| | | import org.forgerock.util.promise.Function; |
| | | import org.forgerock.util.promise.Promise; |
| | | import org.forgerock.util.promise.SuccessHandler; |
| | | |
| | | import static org.forgerock.opendj.ldap.spi.LdapPromises.*; |
| | | |
| | | /** |
| | | * Provides a {@link Promise} wrapper and a {@link LdapPromise} implementation. |
| | | * |
| | | * |
| | | * This wrapper allows client code to return {@link LdapPromise} instance when |
| | | * using {@link Promise} chaining methods (e.g onSuccess(), then(), thenAsync()). |
| | | * Wrapping is specially needed with {@link Promise} method which are not returning the original promise. |
| | | * |
| | | * @param <R> |
| | | * The type of the task's result. |
| | | * @param <P> |
| | | * The wrapped promise. |
| | | */ |
| | | class LdapPromiseWrapper<R, P extends Promise<R, LdapException>> implements LdapPromise<R> { |
| | | private final P wrappedPromise; |
| | | private final int requestID; |
| | | |
| | | public LdapPromiseWrapper(P wrappedPromise, int requestID) { |
| | | this.wrappedPromise = wrappedPromise; |
| | | this.requestID = requestID; |
| | | } |
| | | |
| | | @Override |
| | | public int getRequestID() { |
| | | return wrappedPromise instanceof LdapPromise ? ((LdapPromise<R>) wrappedPromise).getRequestID() |
| | | : requestID; |
| | | } |
| | | |
| | | @Override |
| | | public boolean cancel(boolean mayInterruptIfRunning) { |
| | | return wrappedPromise.cancel(mayInterruptIfRunning); |
| | | } |
| | | |
| | | @Override |
| | | public R get() throws ExecutionException, InterruptedException { |
| | | return wrappedPromise.get(); |
| | | } |
| | | |
| | | @Override |
| | | public R get(long timeout, TimeUnit unit) throws ExecutionException, TimeoutException, InterruptedException { |
| | | return wrappedPromise.get(timeout, unit); |
| | | } |
| | | |
| | | @Override |
| | | public R getOrThrow() throws InterruptedException, LdapException { |
| | | return wrappedPromise.getOrThrow(); |
| | | } |
| | | |
| | | @Override |
| | | public R getOrThrow(long timeout, TimeUnit unit) throws InterruptedException, LdapException, TimeoutException { |
| | | return wrappedPromise.getOrThrow(timeout, unit); |
| | | } |
| | | |
| | | @Override |
| | | public R getOrThrowUninterruptibly() throws LdapException { |
| | | return wrappedPromise.getOrThrowUninterruptibly(); |
| | | } |
| | | |
| | | @Override |
| | | public R getOrThrowUninterruptibly(long timeout, TimeUnit unit) throws LdapException, TimeoutException { |
| | | return wrappedPromise.getOrThrowUninterruptibly(timeout, unit); |
| | | } |
| | | |
| | | @Override |
| | | public boolean isCancelled() { |
| | | return wrappedPromise.isCancelled(); |
| | | } |
| | | |
| | | @Override |
| | | public boolean isDone() { |
| | | return wrappedPromise.isDone(); |
| | | } |
| | | |
| | | @Override |
| | | public LdapPromise<R> onFailure(FailureHandler<? super LdapException> onFailure) { |
| | | wrappedPromise.onFailure(onFailure); |
| | | return this; |
| | | } |
| | | |
| | | @Override |
| | | public LdapPromise<R> onSuccess(SuccessHandler<? super R> onSuccess) { |
| | | wrappedPromise.onSuccess(onSuccess); |
| | | return this; |
| | | } |
| | | |
| | | @Override |
| | | public LdapPromise<R> onSuccessOrFailure(Runnable onSuccessOrFailure) { |
| | | wrappedPromise.onSuccessOrFailure(onSuccessOrFailure); |
| | | return this; |
| | | } |
| | | |
| | | @Override |
| | | // @Checkstyle:ignore |
| | | public <VOUT> LdapPromise<VOUT> then(Function<? super R, VOUT, LdapException> onSuccess) { |
| | | return LdapPromises.wrap(wrappedPromise.then(onSuccess), getRequestID()); |
| | | } |
| | | |
| | | @Override |
| | | // @Checkstyle:ignore |
| | | public <VOUT, EOUT extends Exception> Promise<VOUT, EOUT> then(Function<? super R, VOUT, EOUT> onSuccess, |
| | | Function<? super LdapException, VOUT, EOUT> onFailure) { |
| | | return wrappedPromise.then(onSuccess, onFailure); |
| | | } |
| | | |
| | | @Override |
| | | public LdapPromise<R> then(SuccessHandler<? super R> onSuccess) { |
| | | wrappedPromise.then(onSuccess); |
| | | return this; |
| | | } |
| | | |
| | | @Override |
| | | public LdapPromise<R> then(SuccessHandler<? super R> onSuccess, |
| | | FailureHandler<? super LdapException> onFailure) { |
| | | wrappedPromise.then(onSuccess, onFailure); |
| | | return this; |
| | | } |
| | | |
| | | @Override |
| | | public LdapPromise<R> thenAlways(Runnable onSuccessOrFailure) { |
| | | wrappedPromise.thenAlways(onSuccessOrFailure); |
| | | return this; |
| | | } |
| | | |
| | | @Override |
| | | // @Checkstyle:ignore |
| | | public <VOUT> LdapPromise<VOUT> thenAsync(AsyncFunction<? super R, VOUT, LdapException> onSuccess) { |
| | | return wrap(wrappedPromise.thenAsync(onSuccess), getRequestID()); |
| | | } |
| | | |
| | | @Override |
| | | // @Checkstyle:ignore |
| | | public <VOUT, EOUT extends Exception> Promise<VOUT, EOUT> thenAsync( |
| | | AsyncFunction<? super R, VOUT, EOUT> onSuccess, |
| | | AsyncFunction<? super LdapException, VOUT, EOUT> onFailure) { |
| | | return wrappedPromise.thenAsync(onSuccess, onFailure); |
| | | } |
| | | |
| | | public P getWrappedPromise() { |
| | | return wrappedPromise; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 legal-notices/CDDLv1_0.txt |
| | | * or http://forgerock.org/license/CDDLv1.0.html. |
| | | * 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 legal-notices/CDDLv1_0.txt. |
| | | * 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 2014 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.ldap.spi; |
| | | |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.IntermediateResponseHandler; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LdapPromise; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.SearchResultHandler; |
| | | import org.forgerock.opendj.ldap.requests.BindClient; |
| | | import org.forgerock.opendj.ldap.requests.BindRequest; |
| | | import org.forgerock.opendj.ldap.requests.CompareRequest; |
| | | import org.forgerock.opendj.ldap.requests.ExtendedRequest; |
| | | import org.forgerock.opendj.ldap.requests.Request; |
| | | import org.forgerock.opendj.ldap.requests.SearchRequest; |
| | | import org.forgerock.opendj.ldap.responses.CompareResult; |
| | | import org.forgerock.opendj.ldap.responses.ExtendedResult; |
| | | import org.forgerock.opendj.ldap.responses.Result; |
| | | import org.forgerock.util.promise.Promise; |
| | | import org.forgerock.util.promise.Promises; |
| | | |
| | | import static org.forgerock.opendj.ldap.responses.Responses.*; |
| | | |
| | | /** |
| | | * Utility methods for creating and composing {@link LdapPromise}s. |
| | | */ |
| | | public final class LdapPromises { |
| | | /** |
| | | * Returns a {@link LdapPromise} representing an asynchronous task which has |
| | | * already succeeded with the provided result. Attempts to get the result |
| | | * will immediately return the result. |
| | | * |
| | | * @param <R> |
| | | * The type of the task's result, or {@link Void} if the task |
| | | * does not return anything (i.e. it only has side-effects). |
| | | * @param result |
| | | * The result of the asynchronous task. |
| | | * @return A {@link LdapPromise} representing an asynchronous task which has |
| | | * already succeeded with the provided result. |
| | | */ |
| | | public static <R> LdapPromise<R> newSuccessfulLdapPromise(final R result) { |
| | | return wrap(Promises.<R, LdapException> newSuccessfulPromise(result), -1); |
| | | } |
| | | |
| | | /** |
| | | * Returns a {@link LdapPromise} representing an asynchronous task, |
| | | * identified by the provided requestID, which has already succeeded with |
| | | * the provided result. Attempts to get the result will immediately return |
| | | * the result. |
| | | * |
| | | * @param <R> |
| | | * The type of the task's result, or {@link Void} if the task |
| | | * does not return anything (i.e. it only has side-effects). |
| | | * @param result |
| | | * The result of the asynchronous task. |
| | | * @param requestID |
| | | * The request ID of the succeeded task. |
| | | * @return A {@link LdapPromise} representing an asynchronous task which has |
| | | * already succeeded with the provided result. |
| | | */ |
| | | public static <R> LdapPromise<R> newSuccessfulLdapPromise(final R result, int requestID) { |
| | | return wrap(Promises.<R, LdapException> newSuccessfulPromise(result), requestID); |
| | | } |
| | | |
| | | /** |
| | | * Returns a {@link LdapPromise} representing an asynchronous task which has |
| | | * already failed with the provided error. |
| | | * |
| | | * @param <R> |
| | | * The type of the task's result, or {@link Void} if the task |
| | | * does not return anything (i.e. it only has side-effects). |
| | | * @param <E> |
| | | * The type of the exception thrown by the task if it fails. |
| | | * @param error |
| | | * The exception indicating why the asynchronous task has failed. |
| | | * @return A {@link LdapPromise} representing an asynchronous task which has |
| | | * already failed with the provided error. |
| | | */ |
| | | public static <R, E extends LdapException> LdapPromise<R> newFailedLdapPromise(final E error) { |
| | | return wrap(Promises.<R, LdapException> newFailedPromise(error), -1); |
| | | } |
| | | |
| | | /** |
| | | * Returns a {@link LdapPromise} representing an asynchronous task, |
| | | * identified by the provided requestID, which has already failed with the |
| | | * provided error. |
| | | * |
| | | * @param <R> |
| | | * The type of the task's result, or {@link Void} if the task |
| | | * does not return anything (i.e. it only has side-effects). |
| | | * @param <E> |
| | | * The type of the exception thrown by the task if it fails. |
| | | * @param error |
| | | * The exception indicating why the asynchronous task has failed. |
| | | * @param requestID |
| | | * The request ID of the failed task. |
| | | * @return A {@link LdapPromise} representing an asynchronous task which has |
| | | * already failed with the provided error. |
| | | */ |
| | | public static <R, E extends LdapException> LdapPromise<R> newFailedLdapPromise(final E error, int requestID) { |
| | | return wrap(Promises.<R, LdapException> newFailedPromise(error), requestID); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new {@link ResultLdapPromiseImpl} to handle a standard request (add, delete, modify and modidyDN). |
| | | * |
| | | * @param <R> |
| | | * The type of the task's request. |
| | | * |
| | | * @param requestID |
| | | * Identifier of the request. |
| | | * @param request |
| | | * The request sent to the server. |
| | | * @param intermediateResponseHandler |
| | | * Handler that consumes intermediate responses from extended operations. |
| | | * @param connection |
| | | * The connection to directory server. |
| | | * |
| | | * @return The new {@link ResultLdapPromiseImpl}. |
| | | */ |
| | | public static <R extends Request> ResultLdapPromiseImpl<R, Result> newResultLdapPromise(final int requestID, |
| | | final R request, final IntermediateResponseHandler intermediateResponseHandler, |
| | | final Connection connection) { |
| | | return new ResultLdapPromiseImpl<R, Result>(requestID, request, intermediateResponseHandler, connection) { |
| | | @Override |
| | | protected Result newErrorResult(ResultCode resultCode, String diagnosticMessage, Throwable cause) { |
| | | return newResult(resultCode).setDiagnosticMessage(diagnosticMessage).setCause(cause); |
| | | } |
| | | }; |
| | | } |
| | | |
| | | /** |
| | | * Creates a new bind {@link BindResultLdapPromiseImpl}. |
| | | * |
| | | * @param requestID |
| | | * Identifier of the request. |
| | | * @param request |
| | | * The bind request sent to server. |
| | | * @param bindClient |
| | | * Client that binds to the server. |
| | | * @param intermediateResponseHandler |
| | | * Handler that consumes intermediate responses from extended operations. |
| | | * @param connection |
| | | * The connection to directory server. |
| | | * |
| | | * @return The new {@link BindResultLdapPromiseImpl}. |
| | | */ |
| | | public static BindResultLdapPromiseImpl newBindLdapPromise(final int requestID, |
| | | final BindRequest request, final BindClient bindClient, |
| | | final IntermediateResponseHandler intermediateResponseHandler, final Connection connection) { |
| | | return new BindResultLdapPromiseImpl(requestID, request, bindClient, intermediateResponseHandler, connection); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new compare {@link ResultLdapPromiseImpl}. |
| | | * |
| | | * @param requestID |
| | | * Identifier of the request. |
| | | * @param request |
| | | * The compare request sent to the server. |
| | | * @param intermediateResponseHandler |
| | | * Handler that consumes intermediate responses from extended operations. |
| | | * @param connection |
| | | * The connection to directory server. |
| | | * |
| | | * @return The new {@link ResultLdapPromiseImpl}. |
| | | */ |
| | | public static ResultLdapPromiseImpl<CompareRequest, CompareResult> newCompareLdapPromise(final int requestID, |
| | | final CompareRequest request, final IntermediateResponseHandler intermediateResponseHandler, |
| | | final Connection connection) { |
| | | return new ResultLdapPromiseImpl<CompareRequest, CompareResult>(requestID, request, intermediateResponseHandler, |
| | | connection) { |
| | | @Override |
| | | protected CompareResult newErrorResult(ResultCode resultCode, String diagnosticMessage, Throwable cause) { |
| | | return newCompareResult(resultCode).setDiagnosticMessage(diagnosticMessage).setCause(cause); |
| | | } |
| | | }; |
| | | } |
| | | |
| | | /** |
| | | * Creates a new extended {@link ExtendedResultLdapPromiseImpl}. |
| | | * |
| | | * @param <S> |
| | | * The type of result returned by this promise. |
| | | * @param requestID |
| | | * Identifier of the request. |
| | | * @param request |
| | | * The extended request sent to the server. |
| | | * @param intermediateResponseHandler |
| | | * Handler that consumes intermediate responses from extended operations. |
| | | * @param connection |
| | | * The connection to directory server. |
| | | * |
| | | * @return The new {@link ExtendedResultLdapPromiseImpl}. |
| | | */ |
| | | public static <S extends ExtendedResult> ExtendedResultLdapPromiseImpl<S> newExtendedLdapPromise( |
| | | final int requestID, final ExtendedRequest<S> request, |
| | | final IntermediateResponseHandler intermediateResponseHandler, final Connection connection) { |
| | | return new ExtendedResultLdapPromiseImpl<S>(requestID, request, intermediateResponseHandler, connection); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new search {@link SearchResultLdapPromiseImpl}. |
| | | * |
| | | * @param requestID |
| | | * Identifier of the request. |
| | | * @param request |
| | | * The search request sent to the server. |
| | | * @param resultHandler |
| | | * Handler that consumes search result. |
| | | * @param intermediateResponseHandler |
| | | * Handler that consumes intermediate responses from extended operations. |
| | | * @param connection |
| | | * The connection to directory server. |
| | | * |
| | | * @return The new {@link SearchResultLdapPromiseImpl}. |
| | | */ |
| | | public static SearchResultLdapPromiseImpl newSearchLdapPromise(final int requestID, final SearchRequest request, |
| | | final SearchResultHandler resultHandler, final IntermediateResponseHandler intermediateResponseHandler, |
| | | final Connection connection) { |
| | | return new SearchResultLdapPromiseImpl(requestID, request, resultHandler, intermediateResponseHandler, |
| | | connection); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Converts a {@link Promise} to a {@link LdapPromise}. |
| | | * |
| | | * @param <R> |
| | | * The type of the task's result, or {@link Void} if the task |
| | | * does not return anything (i.e. it only has side-effects). |
| | | * @param wrappedPromise |
| | | * The {@link Promise} to wrap. |
| | | * @return A {@link LdapPromise} representing the same asynchronous task as |
| | | * the {@link Promise} provided. |
| | | */ |
| | | public static <R> LdapPromise<R> asPromise(Promise<R, LdapException> wrappedPromise) { |
| | | return wrap(wrappedPromise, -1); |
| | | } |
| | | |
| | | static <R> LdapPromise<R> wrap(Promise<R, LdapException> wrappedPromise, int requestID) { |
| | | return new LdapPromiseWrapper<R, Promise<R, LdapException>>(wrappedPromise, requestID); |
| | | } |
| | | |
| | | private LdapPromises() { |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 legal-notices/CDDLv1_0.txt |
| | | * or http://forgerock.org/license/CDDLv1.0.html. |
| | | * 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 legal-notices/CDDLv1_0.txt. |
| | | * 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 2014 ForgeRock AS. |
| | | */ |
| | | |
| | | package org.forgerock.opendj.ldap.spi; |
| | | |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.IntermediateResponseHandler; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LdapPromise; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.requests.Request; |
| | | import org.forgerock.opendj.ldap.requests.Requests; |
| | | import org.forgerock.opendj.ldap.responses.IntermediateResponse; |
| | | import org.forgerock.opendj.ldap.responses.Result; |
| | | import org.forgerock.util.promise.Promise; |
| | | import org.forgerock.util.promise.PromiseImpl; |
| | | import org.forgerock.util.promise.Promises; |
| | | |
| | | import static org.forgerock.opendj.ldap.LdapException.*; |
| | | |
| | | /** |
| | | * This class provides an implementation of the {@link LdapPromise}. |
| | | * |
| | | * @param <R> |
| | | * The type of the associated {@link Request}. |
| | | * @param <S> |
| | | * The type of result returned by this promise. |
| | | * @see Promise |
| | | * @see Promises |
| | | * @see LdapPromise |
| | | */ |
| | | public abstract class ResultLdapPromiseImpl<R extends Request, S extends Result> extends LdapPromiseImpl<S> |
| | | implements LdapPromise<S>, IntermediateResponseHandler { |
| | | private final R request; |
| | | private IntermediateResponseHandler intermediateResponseHandler; |
| | | private volatile long timestamp; |
| | | |
| | | ResultLdapPromiseImpl(final int requestID, final R request, |
| | | final IntermediateResponseHandler intermediateResponseHandler, final Connection connection) { |
| | | this(new PromiseImpl<S, LdapException>() { |
| | | @Override |
| | | protected final LdapException tryCancel(final boolean mayInterruptIfRunning) { |
| | | /* |
| | | * This will abandon the request, but will also recursively cancel this |
| | | * future. There is no risk of an infinite loop because the state of |
| | | * this future has already been changed. |
| | | */ |
| | | connection.abandonAsync(Requests.newAbandonRequest(requestID)); |
| | | return newLdapException(ResultCode.CLIENT_SIDE_USER_CANCELLED); |
| | | } |
| | | }, requestID, request, intermediateResponseHandler, connection); |
| | | } |
| | | |
| | | ResultLdapPromiseImpl(final PromiseImpl<S, LdapException> impl, final int requestID, final R request, |
| | | final IntermediateResponseHandler intermediateResponseHandler, final Connection connection) { |
| | | super(impl, requestID); |
| | | this.request = request; |
| | | this.intermediateResponseHandler = intermediateResponseHandler; |
| | | this.timestamp = System.currentTimeMillis(); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public final boolean handleIntermediateResponse(final IntermediateResponse response) { |
| | | // FIXME: there's a potential race condition here - the promise could |
| | | // get cancelled between the isDone() call and the handler |
| | | // invocation. We'd need to add support for intermediate handlers in |
| | | // the synchronizer. |
| | | if (!isDone()) { |
| | | updateTimestamp(); |
| | | if (intermediateResponseHandler != null |
| | | && !intermediateResponseHandler.handleIntermediateResponse(response)) { |
| | | intermediateResponseHandler = null; |
| | | } |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * Returns {@code true} if this promise represents the result of a bind or |
| | | * StartTLS request. The default implementation is to return {@code false}. |
| | | * |
| | | * @return {@code true} if this promise represents the result of a bind or |
| | | * StartTLS request. |
| | | */ |
| | | public boolean isBindOrStartTLS() { |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * Returns a string representation of this promise's state. |
| | | * |
| | | * @return String representation of this promise's state. |
| | | */ |
| | | public String toString() { |
| | | final StringBuilder sb = new StringBuilder(); |
| | | sb.append("( requestID = "); |
| | | sb.append(getRequestID()); |
| | | sb.append(" timestamp = "); |
| | | sb.append(timestamp); |
| | | sb.append(" request = "); |
| | | sb.append(getRequest()); |
| | | sb.append(" )"); |
| | | return sb.toString(); |
| | | } |
| | | |
| | | /** |
| | | * Sets the result associated to this promise as an error result. |
| | | * |
| | | * @param result |
| | | * result of an operation |
| | | */ |
| | | public final void adaptErrorResult(final Result result) { |
| | | final S errorResult = newErrorResult(result.getResultCode(), result.getDiagnosticMessage(), result.getCause()); |
| | | setResultOrError(errorResult); |
| | | } |
| | | |
| | | /** |
| | | * Returns the creation time of this promise. |
| | | * |
| | | * @return the timestamp indicating creation time of this promise |
| | | */ |
| | | public final long getTimestamp() { |
| | | return timestamp; |
| | | } |
| | | |
| | | abstract S newErrorResult(ResultCode resultCode, String diagnosticMessage, Throwable cause); |
| | | |
| | | /** |
| | | * Sets the result associated to this promise. |
| | | * |
| | | * @param result |
| | | * the result of operation |
| | | */ |
| | | public final void setResultOrError(final S result) { |
| | | if (result.getResultCode().isExceptional()) { |
| | | getWrappedPromise().handleError(newLdapException(result)); |
| | | } else { |
| | | getWrappedPromise().handleResult(result); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Returns the attached request. |
| | | * |
| | | * @return The request. |
| | | */ |
| | | public R getRequest() { |
| | | return request; |
| | | } |
| | | |
| | | final void updateTimestamp() { |
| | | timestamp = System.currentTimeMillis(); |
| | | } |
| | | |
| | | /** |
| | | * Returns {@code true} if this request should be canceled once the timeout |
| | | * period expires. The default implementation is to return {@code true} |
| | | * which will be appropriate for nearly all requests, the one obvious |
| | | * exception being persistent searches. |
| | | * |
| | | * @return {@code true} if this request should be canceled once the timeout |
| | | * period expires. |
| | | */ |
| | | public boolean checkForTimeout() { |
| | | return true; |
| | | } |
| | | } |
| File was renamed from opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPSearchFutureResultImpl.java |
| | |
| | | import org.forgerock.opendj.ldap.responses.SearchResultReference; |
| | | |
| | | /** |
| | | * Search result future implementation. |
| | | * Search result promise implementation. |
| | | */ |
| | | public final class LDAPSearchFutureResultImpl extends AbstractLDAPFutureResultImpl<Result> implements |
| | | public final class SearchResultLdapPromiseImpl extends ResultLdapPromiseImpl<SearchRequest, Result> implements |
| | | SearchResultHandler { |
| | | private SearchResultHandler searchResultHandler; |
| | | private final SearchRequest request; |
| | | private final boolean isPersistentSearch; |
| | | |
| | | /** |
| | | * Creates a search future result. |
| | | * |
| | | * @param requestID |
| | | * identifier of the request |
| | | * @param request |
| | | * search request |
| | | * @param resultHandler |
| | | * handler that consumes search result |
| | | * @param intermediateResponseHandler |
| | | * handler that consumes intermediate responses from extended |
| | | * operations |
| | | * @param connection |
| | | * the connection to directory server |
| | | */ |
| | | public LDAPSearchFutureResultImpl(final int requestID, final SearchRequest request, |
| | | SearchResultLdapPromiseImpl(final int requestID, final SearchRequest request, |
| | | final SearchResultHandler resultHandler, final IntermediateResponseHandler intermediateResponseHandler, |
| | | final Connection connection) { |
| | | super(requestID, intermediateResponseHandler, connection); |
| | | this.request = request; |
| | | super(requestID, request, intermediateResponseHandler, connection); |
| | | this.searchResultHandler = resultHandler; |
| | | this.isPersistentSearch = |
| | | request.containsControl(PersistentSearchRequestControl.OID) |
| | |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public boolean handleEntry(final SearchResultEntry entry) { |
| | | // FIXME: there's a potential race condition here - the future could |
| | | // FIXME: there's a potential race condition here - the promise could |
| | | // get cancelled between the isDone() call and the handler |
| | | // invocation. We'd need to add support for intermediate handlers in |
| | | // the synchronizer. |
| | |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public boolean handleReference(final SearchResultReference reference) { |
| | | // FIXME: there's a potential race condition here - the future could |
| | | // FIXME: there's a potential race condition here - the promise could |
| | | // get cancelled between the isDone() call and the handler |
| | | // invocation. We'd need to add support for intermediate handlers in |
| | | // the synchronizer. |
| | |
| | | } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | final StringBuilder sb = new StringBuilder(); |
| | | sb.append("LDAPSearchFutureResultImpl("); |
| | | sb.append("request = "); |
| | | sb.append(request); |
| | | super.toString(sb); |
| | | sb.append(")"); |
| | | return sb.toString(); |
| | | } |
| | | |
| | | /** |
| | | * Returns the search request. |
| | | * |
| | | * @return the search request |
| | | */ |
| | | SearchRequest getRequest() { |
| | | return request; |
| | | } |
| | | |
| | | @Override |
| | | protected Result newErrorResult(final ResultCode resultCode, final String diagnosticMessage, |
| | | Result newErrorResult(final ResultCode resultCode, final String diagnosticMessage, |
| | | final Throwable cause) { |
| | | return Responses.newResult(resultCode).setDiagnosticMessage(diagnosticMessage).setCause( |
| | | cause); |
| | | } |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public |
| | | boolean checkForTimeout() { |
| | | public boolean checkForTimeout() { |
| | | // Persistent searches should not time out. |
| | | return !isPersistentSearch; |
| | | } |
| | | |
| | | } |
| | |
| | | * The LDAP options to use when creating connections. |
| | | * @return an implementation of {@code LDAPConnectionFactory} |
| | | */ |
| | | LDAPConnectionFactoryImpl getLDAPConnectionFactory(String host, int port, |
| | | LDAPOptions options); |
| | | LDAPConnectionFactoryImpl getLDAPConnectionFactory(String host, int port, LDAPOptions options); |
| | | |
| | | /** |
| | | * Returns an implementation of {@code LDAPListener}. |
| | |
| | | |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.FutureResult; |
| | | import org.forgerock.opendj.ldap.LdapPromise; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.ResultHandler; |
| | | import org.forgerock.opendj.ldap.SearchResultHandler; |
| | |
| | | } |
| | | |
| | | private final BufferHandler buffer; |
| | | private final FutureResult<Result> future; |
| | | private final LdapPromise<Result> promise; |
| | | private Response nextResponse = null; |
| | | |
| | | /** |
| | |
| | | final BlockingQueue<Response> entries) { |
| | | Reject.ifNull(connection); |
| | | buffer = new BufferHandler(entries); |
| | | future = (FutureResult<Result>) connection.searchAsync(searchRequest, buffer) |
| | | .onSuccess(buffer).onFailure(buffer); |
| | | promise = connection.searchAsync(searchRequest, buffer).onSuccess(buffer).onFailure(buffer); |
| | | } |
| | | |
| | | /** |
| | |
| | | @Override |
| | | public void close() { |
| | | // Cancel the search if it is still running. |
| | | future.cancel(true); |
| | | promise.cancel(true); |
| | | } |
| | | |
| | | /** |
| | |
| | | return false; |
| | | } |
| | | |
| | | throw newErrorResult(result); |
| | | throw newLdapException(result); |
| | | } |
| | | |
| | | /** |
| | |
| | | try { |
| | | nextResponse = buffer.responses.poll(50, TimeUnit.MILLISECONDS); |
| | | } catch (final InterruptedException e) { |
| | | throw newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED, e); |
| | | throw newLdapException(ResultCode.CLIENT_SIDE_USER_CANCELLED, e); |
| | | } |
| | | |
| | | if (nextResponse == null && buffer.isInterrupted) { |
| | |
| | | import static org.fest.assertions.Assertions.*; |
| | | import static org.fest.assertions.Fail.*; |
| | | import static org.forgerock.opendj.ldap.LdapException.*; |
| | | import static org.forgerock.opendj.ldap.FutureResultWrapper.*; |
| | | import static org.forgerock.opendj.ldap.TestCaseUtils.*; |
| | | import static org.forgerock.opendj.ldap.requests.Requests.*; |
| | | import static org.forgerock.opendj.ldap.responses.Responses.*; |
| | | import static org.forgerock.opendj.ldap.spi.LdapPromises.*; |
| | | import static org.mockito.Matchers.*; |
| | | import static org.mockito.Mockito.*; |
| | | |
| | |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public FutureResult<Void> abandonAsync(AbandonRequest request) { |
| | | public LdapPromise<Void> abandonAsync(AbandonRequest request) { |
| | | if (!resultCode.isExceptional()) { |
| | | return newSuccessfulFutureResult((Void) null); |
| | | return newSuccessfulLdapPromise((Void) null); |
| | | } else { |
| | | return newFailedFutureResult(newErrorResult(resultCode)); |
| | | return newFailedLdapPromise(newLdapException(resultCode)); |
| | | } |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public FutureResult<Result> addAsync(AddRequest request, |
| | | public LdapPromise<Result> addAsync(AddRequest request, |
| | | IntermediateResponseHandler intermediateResponseHandler) { |
| | | return getFutureFromResultCode(newResult(resultCode)); |
| | | return getPromiseFromResultCode(newResult(resultCode)); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public FutureResult<BindResult> bindAsync(BindRequest request, |
| | | public LdapPromise<BindResult> bindAsync(BindRequest request, |
| | | IntermediateResponseHandler intermediateResponseHandler) { |
| | | return getFutureFromResultCode(newBindResult(resultCode)); |
| | | return getPromiseFromResultCode(newBindResult(resultCode)); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public FutureResult<CompareResult> compareAsync(CompareRequest request, |
| | | public LdapPromise<CompareResult> compareAsync(CompareRequest request, |
| | | IntermediateResponseHandler intermediateResponseHandler) { |
| | | return getFutureFromResultCode(newCompareResult(resultCode)); |
| | | return getPromiseFromResultCode(newCompareResult(resultCode)); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public FutureResult<Result> deleteAsync(DeleteRequest request, |
| | | public LdapPromise<Result> deleteAsync(DeleteRequest request, |
| | | IntermediateResponseHandler intermediateResponseHandler) { |
| | | return getFutureFromResultCode(newResult(resultCode)); |
| | | return getPromiseFromResultCode(newResult(resultCode)); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(ExtendedRequest<R> request, |
| | | public <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(ExtendedRequest<R> request, |
| | | IntermediateResponseHandler intermediateResponseHandler) { |
| | | return getFutureFromResultCode(request.getResultDecoder().newExtendedErrorResult(resultCode, "", "")); |
| | | return getPromiseFromResultCode(request.getResultDecoder().newExtendedErrorResult(resultCode, "", "")); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public FutureResult<Result> modifyAsync(ModifyRequest request, |
| | | public LdapPromise<Result> modifyAsync(ModifyRequest request, |
| | | IntermediateResponseHandler intermediateResponseHandler) { |
| | | return getFutureFromResultCode(newResult(resultCode)); |
| | | return getPromiseFromResultCode(newResult(resultCode)); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public FutureResult<Result> modifyDNAsync(ModifyDNRequest request, |
| | | public LdapPromise<Result> modifyDNAsync(ModifyDNRequest request, |
| | | IntermediateResponseHandler intermediateResponseHandler) { |
| | | return getFutureFromResultCode(newResult(resultCode)); |
| | | return getPromiseFromResultCode(newResult(resultCode)); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public FutureResult<Result> searchAsync(SearchRequest request, |
| | | public LdapPromise<Result> searchAsync(SearchRequest request, |
| | | IntermediateResponseHandler intermediateResponseHandler, SearchResultHandler entryHandler) { |
| | | for (SearchResultEntry entry : entries) { |
| | | entryHandler.handleEntry(entry); |
| | | } |
| | | |
| | | return getFutureFromResultCode(newResult(resultCode)); |
| | | return getPromiseFromResultCode(newResult(resultCode)); |
| | | } |
| | | |
| | | private <T extends Result> FutureResult<T> getFutureFromResultCode(T correctResult) { |
| | | private <T extends Result> LdapPromise<T> getPromiseFromResultCode(T correctResult) { |
| | | if (resultCode.isExceptional()) { |
| | | return newFailedFutureResult(newErrorResult(resultCode)); |
| | | return newFailedLdapPromise(newLdapException(resultCode)); |
| | | } else { |
| | | return newSuccessfulFutureResult(correctResult); |
| | | return newSuccessfulLdapPromise(correctResult); |
| | | } |
| | | } |
| | | |
| | |
| | | final SearchRequest request = |
| | | newSingleEntrySearchRequest("cn=test", SearchScope.BASE_OBJECT, "(objectClass=*)"); |
| | | SuccessHandler<SearchResultEntry> successHandler = mock(SuccessHandler.class); |
| | | |
| | | FutureResult<SearchResultEntry> futureResult = (FutureResult<SearchResultEntry>) mockConnection |
| | | .searchSingleEntryAsync(request).onSuccess(successHandler); |
| | | |
| | | assertThat(futureResult.get()).isEqualTo(entry); |
| | | SearchResultEntry resultEntry = mockConnection.searchSingleEntryAsync(request).onSuccess(successHandler).get(); |
| | | assertThat(resultEntry).isEqualTo(entry); |
| | | verify(successHandler).handleResult(any(SearchResultEntry.class)); |
| | | } |
| | | |
| | |
| | | * Create load-balancer with two failed connection factories. |
| | | */ |
| | | final ConnectionFactory first = mock(ConnectionFactory.class, "first"); |
| | | final LdapException firstError = newErrorResult(ResultCode.CLIENT_SIDE_SERVER_DOWN); |
| | | final LdapException firstError = newLdapException(ResultCode.CLIENT_SIDE_SERVER_DOWN); |
| | | when(first.getConnection()).thenThrow(firstError); |
| | | |
| | | final ConnectionFactory second = mock(ConnectionFactory.class, "second"); |
| | | final LdapException secondError = newErrorResult(ResultCode.CLIENT_SIDE_SERVER_DOWN); |
| | | final LdapException secondError = newLdapException(ResultCode.CLIENT_SIDE_SERVER_DOWN); |
| | | when(second.getConnection()).thenThrow(secondError); |
| | | |
| | | final ConnectionFactory loadBalancer = |
| | |
| | | */ |
| | | final ConnectionFactory first = mock(ConnectionFactory.class, "first"); |
| | | final ConnectionFactory firstAsync = mockAsync(first); |
| | | final LdapException firstError = newErrorResult(ResultCode.CLIENT_SIDE_SERVER_DOWN); |
| | | final LdapException firstError = newLdapException(ResultCode.CLIENT_SIDE_SERVER_DOWN); |
| | | when(first.getConnection()).thenThrow(firstError).thenReturn(mock(Connection.class)); |
| | | |
| | | final ConnectionFactory second = mock(ConnectionFactory.class, "second"); |
| | | final ConnectionFactory secondAsync = mockAsync(second); |
| | | final LdapException secondError = newErrorResult(ResultCode.CLIENT_SIDE_SERVER_DOWN); |
| | | final LdapException secondError = newLdapException(ResultCode.CLIENT_SIDE_SERVER_DOWN); |
| | | when(second.getConnection()).thenThrow(secondError); |
| | | |
| | | final LoadBalancerEventListener listener = mock(LoadBalancerEventListener.class); |
| | |
| | | connection.addConnectionEventListener(listener); |
| | | assertThat(listeners).hasSize(1); |
| | | listeners.get(0).handleConnectionError(false, |
| | | newErrorResult(ResultCode.CLIENT_SIDE_SERVER_DOWN)); |
| | | newLdapException(ResultCode.CLIENT_SIDE_SERVER_DOWN)); |
| | | verify(listener, times(0)).handleConnectionClosed(); |
| | | verify(listener).handleConnectionError(eq(false), isA(ConnectionException.class)); |
| | | verify(listener, times(0)).handleUnsolicitedNotification(any(ExtendedResult.class)); |
| | |
| | | |
| | | /* |
| | | * Grab another connection and check that this attempt blocks (if there |
| | | * is a connection available immediately then the future will be |
| | | * is a connection available immediately then the promise will be |
| | | * completed immediately). |
| | | */ |
| | | final Promise<? extends Connection, LdapException> future = pool.getConnectionAsync(); |
| | | assertThat(future.isDone()).isFalse(); |
| | | final Promise<? extends Connection, LdapException> promise = pool.getConnectionAsync(); |
| | | assertThat(promise.isDone()).isFalse(); |
| | | |
| | | // Release a connection and verify that it is immediately redeemed by |
| | | // the future. |
| | | // Release a connection and verify that it is immediately redeemed by the promise. |
| | | pc2.close(); |
| | | assertThat(future.isDone()).isTrue(); |
| | | assertThat(promise.isDone()).isTrue(); |
| | | |
| | | // Check that returned connection routes request to released connection. |
| | | final Connection pc3 = future.get(); |
| | | final Connection pc3 = promise.get(); |
| | | assertThat(pc3.bind(bind2).getResultCode()).isEqualTo(ResultCode.SUCCESS); |
| | | verify(factory, times(2)).getConnection(); |
| | | verify(connection2).bind(bind2); |
| | |
| | | } |
| | | |
| | | /** |
| | | * Test that all outstanding pending connection futures are completed when a |
| | | * Test that all outstanding pending connection promises are completed when a |
| | | * connection request fails. |
| | | */ |
| | | @SuppressWarnings({ "rawtypes", "unchecked" }) |
| | | @Test(description = "OPENDJ-1348", timeOut = 10000) |
| | | public void testNewConnectionFailureFlushesAllPendingFutures() throws Exception { |
| | | public void testNewConnectionFailureFlushesAllPendingPromises() throws Exception { |
| | | final ConnectionFactory factory = mock(ConnectionFactory.class); |
| | | final int poolSize = 2; |
| | | final ConnectionPool pool = Connections.newFixedConnectionPool(factory, poolSize); |
| | |
| | | } |
| | | }).when(factory).getConnectionAsync(); |
| | | |
| | | List<Promise<? extends Connection, LdapException>> futures = |
| | | List<Promise<? extends Connection, LdapException>> promises = |
| | | new ArrayList<Promise<? extends Connection, LdapException>>(); |
| | | for (int i = 0; i < poolSize + 1; i++) { |
| | | futures.add(pool.getConnectionAsync()); |
| | | promises.add(pool.getConnectionAsync()); |
| | | } |
| | | // factory.getConnectionAsync() has been called by the pool poolSize times |
| | | verify(factory, times(poolSize)).getConnectionAsync(); |
| | | final LdapException connectError = LdapException.newErrorResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR); |
| | | for (Promise<? extends Connection, LdapException> future : futures) { |
| | | final LdapException connectError = newLdapException(ResultCode.CLIENT_SIDE_CONNECT_ERROR); |
| | | for (Promise<? extends Connection, LdapException> promise : promises) { |
| | | // Simulate that an error happened with the created connections |
| | | ((FutureResultImpl) future).handleError(connectError); |
| | | ((PromiseImpl) promise).handleError(connectError); |
| | | |
| | | try { |
| | | // Before the fix for OPENDJ-1348 the third future.get() would hang. |
| | | future.getOrThrow(); |
| | | // Before the fix for OPENDJ-1348 the third promise.get() would hang. |
| | | promise.getOrThrow(); |
| | | Assert.fail("Expected an exception to be thrown"); |
| | | } catch (LdapException e) { |
| | | assertThat(e).isSameAs(connectError); |
| | |
| | | import org.forgerock.opendj.ldap.requests.BindRequest; |
| | | import org.forgerock.opendj.ldap.requests.SearchRequest; |
| | | import org.forgerock.opendj.ldap.responses.BindResult; |
| | | import org.forgerock.opendj.ldap.responses.Responses; |
| | | import org.forgerock.opendj.ldap.responses.Result; |
| | | import org.forgerock.opendj.ldap.spi.BindResultLdapPromiseImpl; |
| | | import org.forgerock.util.promise.FailureHandler; |
| | | import org.forgerock.util.promise.NeverThrowsException; |
| | | import org.forgerock.util.promise.Promise; |
| | |
| | | import static org.fest.assertions.Assertions.*; |
| | | import static org.fest.assertions.Fail.*; |
| | | import static org.forgerock.opendj.ldap.LdapException.*; |
| | | import static org.forgerock.opendj.ldap.FutureResultWrapper.*; |
| | | import static org.forgerock.opendj.ldap.ResultCode.*; |
| | | import static org.forgerock.opendj.ldap.SearchScope.*; |
| | | import static org.forgerock.opendj.ldap.TestCaseUtils.*; |
| | | import static org.forgerock.opendj.ldap.requests.Requests.*; |
| | | import static org.forgerock.opendj.ldap.responses.Responses.*; |
| | | import static org.forgerock.opendj.ldap.spi.LdapPromiseImpl.*; |
| | | import static org.forgerock.opendj.ldap.spi.LdapPromises.*; |
| | | import static org.mockito.Matchers.*; |
| | | import static org.mockito.Mockito.*; |
| | | |
| | |
| | | * the response once we have attempted a bind. |
| | | */ |
| | | when(connection.searchAsync(any(SearchRequest.class), any(SearchResultHandler.class))).thenReturn( |
| | | FutureResultWrapper.newSuccessfulFutureResult(Responses.newResult(ResultCode.SUCCESS))); |
| | | newSuccessfulLdapPromise(newResult(SUCCESS))); |
| | | when(hbcf.timeSource.currentTimeMillis()).thenReturn(11000L); |
| | | scheduler.runAllTasks(); // Send the heartbeat. |
| | | |
| | |
| | | @Test(description = "OPENDJ-1348") |
| | | public void testBindPreventsHeartBeatTimeout() throws Exception { |
| | | mockConnectionWithInitialHeartbeatResult(ResultCode.SUCCESS); |
| | | mockBindAsyncResponse(); |
| | | BindResultLdapPromiseImpl promise = mockBindAsyncResponse(); |
| | | hbc = hbcf.getConnection(); |
| | | |
| | | /* |
| | |
| | | * the response once we have attempted a heartbeat. |
| | | */ |
| | | when(hbcf.timeSource.currentTimeMillis()).thenReturn(11000L); |
| | | FutureResult<BindResult> future = hbc.bindAsync(newSimpleBindRequest()); |
| | | hbc.bindAsync(newSimpleBindRequest()); |
| | | |
| | | verify(connection, times(1)).bindAsync(any(BindRequest.class), any(IntermediateResponseHandler.class)); |
| | | |
| | |
| | | |
| | | // Send fake bind response, releasing the heartbeat. |
| | | when(hbcf.timeSource.currentTimeMillis()).thenReturn(11099L); |
| | | ((ResultHandler) future).handleResult(newResult(ResultCode.SUCCESS)); |
| | | ((PromiseImpl) promise.getWrappedPromise()).handleResult(newResult(SUCCESS)); |
| | | |
| | | // Check that bind response acts as heartbeat. |
| | | assertThat(hbc.isValid()).isTrue(); |
| | |
| | | @Test |
| | | public void testHeartBeatWhileBindInProgress() throws Exception { |
| | | mockConnectionWithInitialHeartbeatResult(ResultCode.SUCCESS); |
| | | mockBindAsyncResponse(); |
| | | //Trapping the callback of mockedConnection.bindAsync |
| | | BindResultLdapPromiseImpl promise = mockBindAsyncResponse(); |
| | | hbc = hbcf.getConnection(); |
| | | hbc.bindAsync(newSimpleBindRequest()); |
| | | |
| | | /* |
| | | * Send a bind request, trapping the bind call-back so that we can send |
| | | * the response once we have attempted a heartbeat. |
| | | */ |
| | | FutureResult result = hbc.bindAsync(newSimpleBindRequest()); |
| | | |
| | | // Capture the bind result handler. |
| | | verify(connection, times(1)).bindAsync(any(BindRequest.class), any(IntermediateResponseHandler.class)); |
| | | |
| | | /* |
| | |
| | | verify(connection, times(1)).searchAsync(same(HEARTBEAT), any(SearchResultHandler.class)); |
| | | |
| | | // Send fake bind response, releasing the heartbeat. |
| | | ((ResultHandler) result).handleResult(newResult(ResultCode.SUCCESS)); |
| | | ((PromiseImpl) promise.getWrappedPromise()).handleResult(newResult(SUCCESS)); |
| | | |
| | | // Attempt to send a heartbeat again. |
| | | when(hbcf.timeSource.currentTimeMillis()).thenReturn(16000L); |
| | |
| | | |
| | | // Create heart beat connection factory. |
| | | scheduler = new MockScheduler(); |
| | | hbcf = |
| | | new HeartBeatConnectionFactory(factory, 10000, 100, TimeUnit.MILLISECONDS, |
| | | HEARTBEAT, scheduler); |
| | | hbcf = new HeartBeatConnectionFactory(factory, 10000, 100, TimeUnit.MILLISECONDS, HEARTBEAT, scheduler); |
| | | |
| | | // Set initial time stamp. |
| | | hbcf.timeSource = mockTimeSource(0); |
| | | } |
| | | |
| | | private void mockBindAsyncResponse() { |
| | | doAnswer(new Answer<FutureResult<Result>>() { |
| | | private BindResultLdapPromiseImpl mockBindAsyncResponse() { |
| | | final BindResultLdapPromiseImpl bindPromise = newBindLdapPromise(-1, null, null, null, connection); |
| | | doAnswer(new Answer<LdapPromise<BindResult>>() { |
| | | @Override |
| | | public FutureResult<Result> answer(final InvocationOnMock invocation) throws Throwable { |
| | | return new FutureResultImpl<Result>(); |
| | | public LdapPromise<BindResult> answer(final InvocationOnMock invocation) throws Throwable { |
| | | return bindPromise; |
| | | } |
| | | }).when(connection).bindAsync(any(BindRequest.class), any(IntermediateResponseHandler.class)); |
| | | |
| | | return bindPromise; |
| | | } |
| | | |
| | | private Connection mockHeartBeatResponse(final Connection mockConnection, |
| | | final List<ConnectionEventListener> listeners, final ResultCode resultCode) { |
| | | Answer<FutureResult<Result>> answer = new Answer<FutureResult<Result>>() { |
| | | final List<ConnectionEventListener> listeners, final ResultCode resultCode) { |
| | | Answer<LdapPromise<Result>> answer = new Answer<LdapPromise<Result>>() { |
| | | @Override |
| | | public FutureResult<Result> answer(final InvocationOnMock invocation) throws Throwable { |
| | | public LdapPromise<Result> answer(final InvocationOnMock invocation) throws Throwable { |
| | | if (resultCode == null) { |
| | | return null; |
| | | return newLdapPromiseImpl(); |
| | | } |
| | | |
| | | if (resultCode.isExceptional()) { |
| | | final LdapException error = newErrorResult(resultCode); |
| | | final LdapException error = newLdapException(resultCode); |
| | | if (error instanceof ConnectionException) { |
| | | for (final ConnectionEventListener listener : listeners) { |
| | | listener.handleConnectionError(false, error); |
| | | } |
| | | } |
| | | return newFailedFutureResult(error); |
| | | return newFailedLdapPromise(error); |
| | | } else { |
| | | return newSuccessfulFutureResult(newResult(resultCode)); |
| | | return newSuccessfulLdapPromise(newResult(resultCode)); |
| | | } |
| | | } |
| | | }; |
| | |
| | | import com.forgerock.opendj.ldap.controls.AccountUsabilityRequestControl; |
| | | import com.forgerock.opendj.ldap.controls.AccountUsabilityResponseControl; |
| | | |
| | | import static org.forgerock.opendj.ldap.LdapException.*; |
| | | import static org.forgerock.opendj.ldap.TestCaseUtils.*; |
| | | |
| | | /** |
| | |
| | | if (entryMap.containsKey(dn)) { |
| | | // duplicate entry. |
| | | result = Responses.newResult(ResultCode.ENTRY_ALREADY_EXISTS); |
| | | handler.handleError(LdapException.newErrorResult(result)); |
| | | handler.handleError(newLdapException(result)); |
| | | // doesn't matter if it was canceled. |
| | | requestsInProgress.remove(context); |
| | | return; |
| | |
| | | |
| | | if (abReq.isCanceled()) { |
| | | result = Responses.newResult(ResultCode.CANCELLED); |
| | | handler.handleError(LdapException.newErrorResult(result)); |
| | | handler.handleError(newLdapException(result)); |
| | | requestsInProgress.remove(context); |
| | | return; |
| | | } |
| | |
| | | try { |
| | | return saslServer.unwrap(incoming, offset, len); |
| | | } catch (SaslException e) { |
| | | throw LdapException.newErrorResult( |
| | | throw newLdapException( |
| | | Responses.newResult(ResultCode.OPERATIONS_ERROR).setCause(e)); |
| | | } |
| | | } |
| | |
| | | try { |
| | | return saslServer.wrap(outgoing, offset, len); |
| | | } catch (SaslException e) { |
| | | throw LdapException.newErrorResult( |
| | | throw newLdapException( |
| | | Responses.newResult(ResultCode.OPERATIONS_ERROR).setCause(e)); |
| | | } |
| | | } |
| | |
| | | ByteString.wrap(challenge))); |
| | | } |
| | | } catch (Exception e) { |
| | | resultHandler.handleError(LdapException.newErrorResult(Responses |
| | | resultHandler.handleError(newLdapException(Responses |
| | | .newBindResult(ResultCode.OPERATIONS_ERROR).setCause(e) |
| | | .setDiagnosticMessage(e.toString()))); |
| | | } |
| | |
| | | if (!entryMap.containsKey(dn)) { |
| | | // entry not found. |
| | | result = Responses.newCompareResult(ResultCode.NO_SUCH_ATTRIBUTE); |
| | | resultHandler.handleError(LdapException.newErrorResult(result)); |
| | | resultHandler.handleError(newLdapException(result)); |
| | | // doesn't matter if it was canceled. |
| | | requestsInProgress.remove(context); |
| | | return; |
| | |
| | | final ByteString s = it.next(); |
| | | if (abReq.isCanceled()) { |
| | | final Result r = Responses.newResult(ResultCode.CANCELLED); |
| | | resultHandler.handleError(LdapException.newErrorResult(r)); |
| | | resultHandler.handleError(newLdapException(r)); |
| | | requestsInProgress.remove(context); |
| | | return; |
| | | } |
| | |
| | | if (!entryMap.containsKey(dn)) { |
| | | // entry is not found. |
| | | result = Responses.newResult(ResultCode.NO_SUCH_OBJECT); |
| | | handler.handleError(LdapException.newErrorResult(result)); |
| | | handler.handleError(newLdapException(result)); |
| | | // doesn't matter if it was canceled. |
| | | requestsInProgress.remove(context); |
| | | return; |
| | |
| | | |
| | | if (abReq.isCanceled()) { |
| | | result = Responses.newResult(ResultCode.CANCELLED); |
| | | handler.handleError(LdapException.newErrorResult(result)); |
| | | handler.handleError(newLdapException(result)); |
| | | requestsInProgress.remove(context); |
| | | return; |
| | | } |
| | |
| | | if (!entryMap.containsKey(dn)) { |
| | | // Entry not found. |
| | | result = Responses.newResult(ResultCode.NO_SUCH_OBJECT); |
| | | resultHandler.handleError(LdapException.newErrorResult(result)); |
| | | resultHandler.handleError(newLdapException(result)); |
| | | // Should searchResultHandler handle anything? |
| | | |
| | | // doesn't matter if it was canceled. |
| | |
| | | |
| | | if (abReq.isCanceled()) { |
| | | result = Responses.newResult(ResultCode.CANCELLED); |
| | | resultHandler.handleError(LdapException.newErrorResult(result)); |
| | | resultHandler.handleError(newLdapException(result)); |
| | | requestsInProgress.remove(context); |
| | | return; |
| | | } |
| | |
| | | import com.forgerock.opendj.util.TimeSource; |
| | | |
| | | import static org.fest.assertions.Fail.*; |
| | | import static org.forgerock.opendj.ldap.FutureResultWrapper.*; |
| | | import static org.forgerock.opendj.ldap.spi.LdapPromises.*; |
| | | import static org.mockito.Matchers.*; |
| | | import static org.mockito.Mockito.*; |
| | | |
| | |
| | | @Override |
| | | public Promise<Connection, LdapException> answer(final InvocationOnMock invocation) |
| | | throws Throwable { |
| | | return newSuccessfulFutureResult(factory.getConnection()); |
| | | return newSuccessfulLdapPromise(factory.getConnection()); |
| | | } |
| | | }); |
| | | return factory; |
| | |
| | | package org.forgerock.opendj.ldap.schema; |
| | | |
| | | import static org.fest.assertions.Assertions.assertThat; |
| | | import static org.forgerock.opendj.ldap.LdapException.*; |
| | | import static org.forgerock.opendj.ldap.schema.SchemaValidationPolicy.defaultPolicy; |
| | | import static org.forgerock.opendj.ldap.schema.SchemaValidationPolicy.ignoreAll; |
| | | |
| | |
| | | |
| | | public Entry getEntry(final DN dn) throws LdapException { |
| | | if (e == null) { |
| | | throw LdapException.newErrorResult(ResultCode.NO_SUCH_OBJECT, |
| | | throw newLdapException(ResultCode.NO_SUCH_OBJECT, |
| | | "no such entry " + dn.toString()); |
| | | } |
| | | |
| | |
| | | import org.forgerock.opendj.ldap.DecodeException; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.EntryNotFoundException; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.FutureResult; |
| | | import org.forgerock.opendj.ldap.FutureResultWrapper; |
| | | import org.forgerock.opendj.ldap.LdapPromise; |
| | | import org.forgerock.opendj.ldap.LinkedHashMapEntry; |
| | | import org.forgerock.opendj.ldap.requests.SearchRequest; |
| | | import org.forgerock.opendj.ldap.responses.Responses; |
| | | import org.forgerock.opendj.ldap.responses.SearchResultEntry; |
| | | import org.forgerock.util.promise.Promise; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.fest.assertions.Assertions.*; |
| | | import static org.fest.assertions.Fail.*; |
| | | import static org.forgerock.opendj.ldap.schema.SchemaConstants.*; |
| | | import static org.forgerock.util.promise.Promises.*; |
| | | import static org.forgerock.opendj.ldap.spi.LdapPromises.*; |
| | | import static org.mockito.Matchers.*; |
| | | import static org.mockito.Mockito.*; |
| | | |
| | |
| | | }; |
| | | |
| | | // Send a search entry result promise : |
| | | Promise<SearchResultEntry, LdapException> promise = |
| | | newSuccessfulPromise(Responses.newSearchResultEntry(entry)); |
| | | FutureResult<SearchResultEntry> result = FutureResultWrapper.asFutureResult(promise); |
| | | LdapPromise<SearchResultEntry> result = |
| | | newSuccessfulLdapPromise(Responses.newSearchResultEntry(entry)); |
| | | when(connection.searchSingleEntryAsync((SearchRequest) any())).thenReturn(result); |
| | | DN testDN = DN.valueOf("uid=bjensen,ou=People,dc=example,dc=com"); |
| | | // @formatter:on |
| | |
| | | */ |
| | | package org.forgerock.opendj.ldap.schema; |
| | | |
| | | import static org.fest.assertions.Assertions.assertThat; |
| | | import static org.forgerock.util.promise.Promises.*; |
| | | import static org.mockito.Matchers.*; |
| | | import static org.mockito.Mockito.*; |
| | | |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.FutureResult; |
| | | import org.forgerock.opendj.ldap.FutureResultWrapper; |
| | | import org.forgerock.opendj.ldap.LdapPromise; |
| | | import org.forgerock.opendj.ldap.requests.SearchRequest; |
| | | import org.forgerock.opendj.ldap.responses.Responses; |
| | | import org.forgerock.opendj.ldap.responses.SearchResultEntry; |
| | | import org.forgerock.util.promise.Promise; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.fest.assertions.Assertions.*; |
| | | import static org.forgerock.opendj.ldap.spi.LdapPromises.*; |
| | | import static org.mockito.Matchers.*; |
| | | import static org.mockito.Mockito.*; |
| | | |
| | | /** |
| | | * Tests the Schema class. |
| | | */ |
| | |
| | | }; |
| | | |
| | | // Send a search entry result promise : |
| | | Promise<SearchResultEntry, LdapException> promise = |
| | | newSuccessfulPromise(Responses.newSearchResultEntry(entry)); |
| | | FutureResult<SearchResultEntry> result = FutureResultWrapper.asFutureResult(promise); |
| | | LdapPromise<SearchResultEntry> result = newSuccessfulLdapPromise(Responses.newSearchResultEntry(entry)); |
| | | when(connection.searchSingleEntryAsync((SearchRequest) any())).thenReturn(result); |
| | | DN testDN = DN.valueOf("uid=bjensen,ou=People,dc=example,dc=com"); |
| | | // @formatter:on |
| | |
| | | try { |
| | | return getConnectionAsync().getOrThrow(); |
| | | } catch (final InterruptedException e) { |
| | | throw newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED, e); |
| | | throw newLdapException(ResultCode.CLIENT_SIDE_USER_CANCELLED, e); |
| | | } |
| | | } |
| | | |
| | |
| | | package org.forgerock.opendj.ldap.spi; |
| | | |
| | | import static org.fest.assertions.Assertions.assertThat; |
| | | import static org.forgerock.opendj.ldap.LdapException.newErrorResult; |
| | | import static org.forgerock.opendj.ldap.LdapException.newLdapException; |
| | | import static org.forgerock.opendj.ldap.responses.Responses.newGenericExtendedResult; |
| | | import static org.mockito.Matchers.same; |
| | | import static org.mockito.Mockito.doAnswer; |
| | |
| | | */ |
| | | @SuppressWarnings("javadoc") |
| | | public class ConnectionStateTest extends LDAPTestCase { |
| | | private static final LdapException ERROR = newErrorResult(ResultCode.OTHER); |
| | | private static final LdapException LATE_ERROR = newErrorResult(ResultCode.BUSY); |
| | | private static final LdapException ERROR = newLdapException(ResultCode.OTHER); |
| | | private static final LdapException LATE_ERROR = newLdapException(ResultCode.BUSY); |
| | | private static final ExtendedResult UNSOLICITED = |
| | | newGenericExtendedResult(ResultCode.OPERATIONS_ERROR); |
| | | |
| | |
| | | if (handler != null) { |
| | | // Data here if needed. |
| | | } |
| | | return newErrorResult(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION); |
| | | return newLdapException(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION); |
| | | } |
| | | }); |
| | | |
| | |
| | | |
| | | package org.forgerock.opendj.ldif; |
| | | |
| | | import static org.fest.assertions.Assertions.assertThat; |
| | | import static org.fest.assertions.Fail.fail; |
| | | import static org.forgerock.opendj.ldap.LdapException.newErrorResult; |
| | | import static org.forgerock.opendj.ldap.responses.Responses.newResult; |
| | | import static org.forgerock.opendj.ldap.responses.Responses.newSearchResultEntry; |
| | | import static org.forgerock.opendj.ldap.responses.Responses.newSearchResultReference; |
| | | import static org.mockito.Matchers.any; |
| | | import static org.mockito.Matchers.same; |
| | | import static org.mockito.Mockito.mock; |
| | | import static org.mockito.Mockito.when; |
| | | |
| | | import java.util.NoSuchElementException; |
| | | |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.FutureResult; |
| | | import org.forgerock.opendj.ldap.FutureResultWrapper; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LdapPromise; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.SearchResultHandler; |
| | | import org.forgerock.opendj.ldap.SearchResultReferenceIOException; |
| | |
| | | import org.mockito.stubbing.Answer; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.fest.assertions.Assertions.*; |
| | | import static org.fest.assertions.Fail.*; |
| | | import static org.forgerock.opendj.ldap.LdapException.*; |
| | | import static org.forgerock.opendj.ldap.responses.Responses.*; |
| | | import static org.forgerock.opendj.ldap.spi.LdapPromises.*; |
| | | import static org.mockito.Matchers.*; |
| | | import static org.mockito.Mockito.*; |
| | | |
| | | /** |
| | | * This class tests the ConnectionEntryReader functionality. |
| | | */ |
| | |
| | | final Connection connection = mock(Connection.class); |
| | | // @formatter:off |
| | | when(connection.searchAsync(same(SEARCH), any(SearchResultHandler.class))).thenAnswer( |
| | | new Answer<FutureResult<Result>>() { |
| | | new Answer<LdapPromise<Result>>() { |
| | | @Override |
| | | public FutureResult<Result> answer(final InvocationOnMock invocation) throws Throwable { |
| | | public LdapPromise<Result> answer(final InvocationOnMock invocation) throws Throwable { |
| | | // Execute handler and return future. |
| | | final SearchResultHandler handler = (SearchResultHandler) invocation.getArguments()[1]; |
| | | if (handler != null) { |
| | |
| | | } |
| | | final Result result = (Result) responses[responses.length - 1]; |
| | | if (result.isSuccess()) { |
| | | return FutureResultWrapper.newSuccessfulFutureResult(result); |
| | | return newSuccessfulLdapPromise(result); |
| | | } else { |
| | | return FutureResultWrapper.newFailedFutureResult(newErrorResult(result)); |
| | | return newFailedLdapPromise(newLdapException(result)); |
| | | } |
| | | } |
| | | }); |
| | |
| | | public class GrizzlyTransportProvider implements TransportProvider { |
| | | |
| | | @Override |
| | | public LDAPConnectionFactoryImpl getLDAPConnectionFactory(String host, int port, |
| | | LDAPOptions options) { |
| | | public LDAPConnectionFactoryImpl getLDAPConnectionFactory(String host, int port, LDAPOptions options) { |
| | | return new GrizzlyLDAPConnectionFactory(host, port, options); |
| | | } |
| | | |
| | |
| | | import org.forgerock.opendj.ldap.AbstractAsynchronousConnection; |
| | | import org.forgerock.opendj.ldap.ConnectionEventListener; |
| | | import org.forgerock.opendj.ldap.Connections; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.FutureResult; |
| | | import org.forgerock.opendj.ldap.IntermediateResponseHandler; |
| | | import org.forgerock.opendj.ldap.LDAPOptions; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LdapPromise; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.SSLContextBuilder; |
| | | import org.forgerock.opendj.ldap.SearchResultHandler; |
| | |
| | | import org.forgerock.opendj.ldap.responses.ExtendedResult; |
| | | import org.forgerock.opendj.ldap.responses.Responses; |
| | | import org.forgerock.opendj.ldap.responses.Result; |
| | | import org.forgerock.opendj.ldap.spi.AbstractLDAPFutureResultImpl; |
| | | import org.forgerock.opendj.ldap.spi.LDAPBindFutureResultImpl; |
| | | import org.forgerock.opendj.ldap.spi.LDAPCompareFutureResultImpl; |
| | | import org.forgerock.opendj.ldap.spi.LDAPExtendedFutureResultImpl; |
| | | import org.forgerock.opendj.ldap.spi.LDAPFutureResultImpl; |
| | | import org.forgerock.opendj.ldap.spi.LDAPSearchFutureResultImpl; |
| | | import org.forgerock.opendj.ldap.spi.BindResultLdapPromiseImpl; |
| | | import org.forgerock.opendj.ldap.spi.ExtendedResultLdapPromiseImpl; |
| | | import org.forgerock.opendj.ldap.spi.ResultLdapPromiseImpl; |
| | | import org.forgerock.opendj.ldap.spi.SearchResultLdapPromiseImpl; |
| | | import org.forgerock.util.Reject; |
| | | import org.glassfish.grizzly.CompletionHandler; |
| | | import org.glassfish.grizzly.filterchain.Filter; |
| | |
| | | import org.glassfish.grizzly.ssl.SSLEngineConfigurator; |
| | | import org.glassfish.grizzly.ssl.SSLFilter; |
| | | |
| | | import static org.forgerock.opendj.ldap.LdapException.newErrorResult; |
| | | import static org.forgerock.opendj.ldap.FutureResultWrapper.*; |
| | | import static org.forgerock.opendj.ldap.LdapException.*; |
| | | import static org.forgerock.opendj.ldap.spi.LdapPromises.*; |
| | | |
| | | import static com.forgerock.opendj.grizzly.GrizzlyMessages.*; |
| | | |
| | |
| | | private final org.glassfish.grizzly.Connection<?> connection; |
| | | private final AtomicInteger nextMsgID = new AtomicInteger(1); |
| | | private final GrizzlyLDAPConnectionFactory factory; |
| | | private final ConcurrentHashMap<Integer, AbstractLDAPFutureResultImpl<?>> pendingRequests = |
| | | new ConcurrentHashMap<Integer, AbstractLDAPFutureResultImpl<?>>(); |
| | | private final ConcurrentHashMap<Integer, ResultLdapPromiseImpl<?, ?>> pendingRequests = |
| | | new ConcurrentHashMap<Integer, ResultLdapPromiseImpl<?, ?>>(); |
| | | private final Object stateLock = new Object(); |
| | | /** Guarded by stateLock. */ |
| | | private Result connectionInvalidReason; |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Void> abandonAsync(final AbandonRequest request) { |
| | | public LdapPromise<Void> abandonAsync(final AbandonRequest request) { |
| | | /* |
| | | * Need to be careful here since both abandonAsync and Future.cancel can |
| | | * Need to be careful here since both abandonAsync and Promise.cancel can |
| | | * be called separately by the client application. Therefore |
| | | * future.cancel() should abandon the request, and abandonAsync should |
| | | * cancel the future. In addition, bind or StartTLS requests cannot be |
| | | * promise.cancel() should abandon the request, and abandonAsync should |
| | | * cancel the promise. In addition, bind or StartTLS requests cannot be |
| | | * abandoned. |
| | | */ |
| | | try { |
| | |
| | | checkBindOrStartTLSInProgress(); |
| | | } |
| | | } catch (final LdapException e) { |
| | | return newFailedFutureResult(e); |
| | | return newFailedLdapPromise(e); |
| | | } |
| | | |
| | | // Remove the future associated with the request to be abandoned. |
| | | final AbstractLDAPFutureResultImpl<?> pendingRequest = pendingRequests.remove(request.getRequestID()); |
| | | // Remove the promise associated with the request to be abandoned. |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = pendingRequests.remove(request.getRequestID()); |
| | | if (pendingRequest == null) { |
| | | /* |
| | | * There has never been a request with the specified message ID or |
| | | * the response has already been received and handled. We can ignore |
| | | * this abandon request. |
| | | */ |
| | | return newSuccessfulFutureResult((Void) null); |
| | | return newSuccessfulLdapPromise((Void) null); |
| | | } |
| | | |
| | | /* |
| | | * This will cancel the future, but will also recursively invoke this |
| | | * This will cancel the promise, but will also recursively invoke this |
| | | * method. Since the pending request has been removed, there is no risk |
| | | * of an infinite loop. |
| | | */ |
| | |
| | | return sendAbandonRequest(request); |
| | | } |
| | | |
| | | private FutureResult<Void> sendAbandonRequest(final AbandonRequest request) { |
| | | private LdapPromise<Void> sendAbandonRequest(final AbandonRequest request) { |
| | | final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter(); |
| | | try { |
| | | final int messageID = nextMsgID.getAndIncrement(); |
| | | writer.writeAbandonRequest(messageID, request); |
| | | connection.write(writer.getASN1Writer().getBuffer(), null); |
| | | return newSuccessfulFutureResult((Void) null, messageID); |
| | | return newSuccessfulLdapPromise((Void) null, messageID); |
| | | } catch (final IOException e) { |
| | | return newFailedFutureResult(adaptRequestIOException(e)); |
| | | return newFailedLdapPromise(adaptRequestIOException(e)); |
| | | } finally { |
| | | GrizzlyUtils.recycleWriter(writer); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> addAsync(final AddRequest request, |
| | | public LdapPromise<Result> addAsync(final AddRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | final int messageID = nextMsgID.getAndIncrement(); |
| | | final LDAPFutureResultImpl future = |
| | | new LDAPFutureResultImpl(messageID, request, intermediateResponseHandler, this); |
| | | final ResultLdapPromiseImpl<AddRequest, Result> promise = |
| | | newResultLdapPromise(messageID, request, intermediateResponseHandler, this); |
| | | try { |
| | | synchronized (stateLock) { |
| | | checkConnectionIsValid(); |
| | | checkBindOrStartTLSInProgress(); |
| | | pendingRequests.put(messageID, future); |
| | | pendingRequests.put(messageID, promise); |
| | | } |
| | | try { |
| | | final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter(); |
| | |
| | | throw adaptRequestIOException(e); |
| | | } |
| | | } catch (final LdapException e) { |
| | | future.adaptErrorResult(e.getResult()); |
| | | promise.adaptErrorResult(e.getResult()); |
| | | } |
| | | return future; |
| | | return promise; |
| | | } |
| | | |
| | | @Override |
| | |
| | | if (notifyErrorOccurred) { |
| | | // Use the reason provided in the disconnect notification. |
| | | listener.handleConnectionError(failedDueToDisconnect, |
| | | newErrorResult(connectionInvalidReason)); |
| | | newLdapException(connectionInvalidReason)); |
| | | } |
| | | if (notifyClose) { |
| | | listener.handleConnectionClosed(); |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<BindResult> bindAsync(final BindRequest request, |
| | | public LdapPromise<BindResult> bindAsync(final BindRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | final int messageID = nextMsgID.getAndIncrement(); |
| | | final BindClient context; |
| | | try { |
| | | context = request.createBindClient(Connections.getHostString(factory.getSocketAddress())); |
| | | } catch (final Exception e) { |
| | | // FIXME: I18N need to have a better error message. |
| | | // FIXME: Is this the best result code? |
| | | final Result errorResult = Responses.newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR) |
| | | .setDiagnosticMessage("An error occurred while creating a bind context").setCause(e); |
| | | final LdapException error = LdapException.newErrorResult(errorResult); |
| | | |
| | | return newFailedFutureResult(error, messageID); |
| | | } catch (final LdapException e) { |
| | | return newFailedLdapPromise(e, messageID); |
| | | } |
| | | |
| | | final LDAPBindFutureResultImpl future = |
| | | new LDAPBindFutureResultImpl(messageID, context, intermediateResponseHandler, this); |
| | | final BindResultLdapPromiseImpl promise = |
| | | newBindLdapPromise(messageID, request, context, intermediateResponseHandler, this); |
| | | |
| | | try { |
| | | synchronized (stateLock) { |
| | | checkConnectionIsValid(); |
| | | if (!pendingRequests.isEmpty()) { |
| | | future.setResultOrError(Responses.newBindResult(ResultCode.OPERATIONS_ERROR).setDiagnosticMessage( |
| | | promise.setResultOrError(Responses.newBindResult(ResultCode.OPERATIONS_ERROR).setDiagnosticMessage( |
| | | "There are other operations pending on this connection")); |
| | | return future; |
| | | return promise; |
| | | } |
| | | if (!bindOrStartTLSInProgress.compareAndSet(false, true)) { |
| | | future.setResultOrError(Responses.newBindResult(ResultCode.OPERATIONS_ERROR).setDiagnosticMessage( |
| | | promise.setResultOrError(Responses.newBindResult(ResultCode.OPERATIONS_ERROR).setDiagnosticMessage( |
| | | "Bind or Start TLS operation in progress")); |
| | | return future; |
| | | return promise; |
| | | } |
| | | pendingRequests.put(messageID, future); |
| | | pendingRequests.put(messageID, promise); |
| | | } |
| | | |
| | | try { |
| | |
| | | throw adaptRequestIOException(e); |
| | | } |
| | | } catch (final LdapException e) { |
| | | future.adaptErrorResult(e.getResult()); |
| | | promise.adaptErrorResult(e.getResult()); |
| | | } |
| | | |
| | | return future; |
| | | return promise; |
| | | } |
| | | |
| | | @Override |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<CompareResult> compareAsync(final CompareRequest request, |
| | | public LdapPromise<CompareResult> compareAsync(final CompareRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | final int messageID = nextMsgID.getAndIncrement(); |
| | | final LDAPCompareFutureResultImpl future = |
| | | new LDAPCompareFutureResultImpl(messageID, request, intermediateResponseHandler, this); |
| | | final ResultLdapPromiseImpl<CompareRequest, CompareResult> promise = |
| | | newCompareLdapPromise(messageID, request, intermediateResponseHandler, this); |
| | | try { |
| | | synchronized (stateLock) { |
| | | checkConnectionIsValid(); |
| | | checkBindOrStartTLSInProgress(); |
| | | pendingRequests.put(messageID, future); |
| | | pendingRequests.put(messageID, promise); |
| | | } |
| | | try { |
| | | final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter(); |
| | |
| | | throw adaptRequestIOException(e); |
| | | } |
| | | } catch (final LdapException e) { |
| | | future.adaptErrorResult(e.getResult()); |
| | | promise.adaptErrorResult(e.getResult()); |
| | | } |
| | | return future; |
| | | return promise; |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> deleteAsync(final DeleteRequest request, |
| | | public LdapPromise<Result> deleteAsync(final DeleteRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | final int messageID = nextMsgID.getAndIncrement(); |
| | | final LDAPFutureResultImpl future = |
| | | new LDAPFutureResultImpl(messageID, request, intermediateResponseHandler, this); |
| | | final ResultLdapPromiseImpl<DeleteRequest, Result> promise = |
| | | newResultLdapPromise(messageID, request, intermediateResponseHandler, this); |
| | | try { |
| | | synchronized (stateLock) { |
| | | checkConnectionIsValid(); |
| | | checkBindOrStartTLSInProgress(); |
| | | pendingRequests.put(messageID, future); |
| | | pendingRequests.put(messageID, promise); |
| | | } |
| | | try { |
| | | final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter(); |
| | |
| | | throw adaptRequestIOException(e); |
| | | } |
| | | } catch (final LdapException e) { |
| | | future.adaptErrorResult(e.getResult()); |
| | | promise.adaptErrorResult(e.getResult()); |
| | | } |
| | | return future; |
| | | return promise; |
| | | } |
| | | |
| | | @Override |
| | | public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(final ExtendedRequest<R> request, |
| | | public <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(final ExtendedRequest<R> request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | final int messageID = nextMsgID.getAndIncrement(); |
| | | final LDAPExtendedFutureResultImpl<R> future = |
| | | new LDAPExtendedFutureResultImpl<R>(messageID, request, intermediateResponseHandler, this); |
| | | final ExtendedResultLdapPromiseImpl<R> promise = |
| | | newExtendedLdapPromise(messageID, request, intermediateResponseHandler, this); |
| | | try { |
| | | synchronized (stateLock) { |
| | | checkConnectionIsValid(); |
| | | if (StartTLSExtendedRequest.OID.equals(request.getOID())) { |
| | | if (!pendingRequests.isEmpty()) { |
| | | future.setResultOrError(request.getResultDecoder().newExtendedErrorResult( |
| | | promise.setResultOrError(request.getResultDecoder().newExtendedErrorResult( |
| | | ResultCode.OPERATIONS_ERROR, "", "There are pending operations on this connection")); |
| | | return future; |
| | | return promise; |
| | | } else if (isTLSEnabled()) { |
| | | future.setResultOrError(request.getResultDecoder().newExtendedErrorResult( |
| | | promise.setResultOrError(request.getResultDecoder().newExtendedErrorResult( |
| | | ResultCode.OPERATIONS_ERROR, "", "This connection is already TLS enabled")); |
| | | return future; |
| | | return promise; |
| | | } else if (!bindOrStartTLSInProgress.compareAndSet(false, true)) { |
| | | future.setResultOrError(request.getResultDecoder().newExtendedErrorResult( |
| | | promise.setResultOrError(request.getResultDecoder().newExtendedErrorResult( |
| | | ResultCode.OPERATIONS_ERROR, "", "Bind or Start TLS operation in progress")); |
| | | return future; |
| | | return promise; |
| | | } |
| | | } else { |
| | | checkBindOrStartTLSInProgress(); |
| | | } |
| | | pendingRequests.put(messageID, future); |
| | | pendingRequests.put(messageID, promise); |
| | | } |
| | | try { |
| | | final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter(); |
| | |
| | | throw adaptRequestIOException(e); |
| | | } |
| | | } catch (final LdapException e) { |
| | | future.adaptErrorResult(e.getResult()); |
| | | promise.adaptErrorResult(e.getResult()); |
| | | } |
| | | return future; |
| | | return promise; |
| | | } |
| | | |
| | | @Override |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> modifyAsync(final ModifyRequest request, |
| | | public LdapPromise<Result> modifyAsync(final ModifyRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | final int messageID = nextMsgID.getAndIncrement(); |
| | | final LDAPFutureResultImpl future = |
| | | new LDAPFutureResultImpl(messageID, request, intermediateResponseHandler, this); |
| | | final ResultLdapPromiseImpl<ModifyRequest, Result> promise = |
| | | newResultLdapPromise(messageID, request, intermediateResponseHandler, this); |
| | | try { |
| | | synchronized (stateLock) { |
| | | checkConnectionIsValid(); |
| | | checkBindOrStartTLSInProgress(); |
| | | pendingRequests.put(messageID, future); |
| | | pendingRequests.put(messageID, promise); |
| | | } |
| | | try { |
| | | final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter(); |
| | |
| | | throw adaptRequestIOException(e); |
| | | } |
| | | } catch (final LdapException e) { |
| | | future.adaptErrorResult(e.getResult()); |
| | | promise.adaptErrorResult(e.getResult()); |
| | | } |
| | | return future; |
| | | return promise; |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request, |
| | | public LdapPromise<Result> modifyDNAsync(final ModifyDNRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | final int messageID = nextMsgID.getAndIncrement(); |
| | | final LDAPFutureResultImpl future = |
| | | new LDAPFutureResultImpl(messageID, request, intermediateResponseHandler, this); |
| | | final ResultLdapPromiseImpl<ModifyDNRequest, Result> promise = |
| | | newResultLdapPromise(messageID, request, intermediateResponseHandler, this); |
| | | try { |
| | | synchronized (stateLock) { |
| | | checkConnectionIsValid(); |
| | | checkBindOrStartTLSInProgress(); |
| | | pendingRequests.put(messageID, future); |
| | | pendingRequests.put(messageID, promise); |
| | | } |
| | | try { |
| | | final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter(); |
| | |
| | | throw adaptRequestIOException(e); |
| | | } |
| | | } catch (final LdapException e) { |
| | | future.adaptErrorResult(e.getResult()); |
| | | promise.adaptErrorResult(e.getResult()); |
| | | } |
| | | return future; |
| | | return promise; |
| | | } |
| | | |
| | | @Override |
| | |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public FutureResult<Result> searchAsync(final SearchRequest request, |
| | | public LdapPromise<Result> searchAsync(final SearchRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler, final SearchResultHandler entryHandler) { |
| | | final int messageID = nextMsgID.getAndIncrement(); |
| | | final LDAPSearchFutureResultImpl future = |
| | | new LDAPSearchFutureResultImpl(messageID, request, entryHandler, intermediateResponseHandler, this); |
| | | final SearchResultLdapPromiseImpl promise = |
| | | newSearchLdapPromise(messageID, request, entryHandler, intermediateResponseHandler, this); |
| | | try { |
| | | synchronized (stateLock) { |
| | | checkConnectionIsValid(); |
| | | checkBindOrStartTLSInProgress(); |
| | | pendingRequests.put(messageID, future); |
| | | pendingRequests.put(messageID, promise); |
| | | } |
| | | try { |
| | | final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter(); |
| | |
| | | throw adaptRequestIOException(e); |
| | | } |
| | | } catch (final LdapException e) { |
| | | future.adaptErrorResult(e.getResult()); |
| | | promise.adaptErrorResult(e.getResult()); |
| | | } |
| | | return future; |
| | | return promise; |
| | | } |
| | | |
| | | @Override |
| | |
| | | } |
| | | |
| | | long delay = timeout; |
| | | for (final AbstractLDAPFutureResultImpl<?> future : pendingRequests.values()) { |
| | | if (future == null || !future.checkForTimeout()) { |
| | | for (final ResultLdapPromiseImpl<?, ?> promise : pendingRequests.values()) { |
| | | if (promise == null || !promise.checkForTimeout()) { |
| | | continue; |
| | | } |
| | | final long diff = (future.getTimestamp() + timeout) - currentTime; |
| | | final long diff = (promise.getTimestamp() + timeout) - currentTime; |
| | | if (diff > 0) { |
| | | // Will expire in diff milliseconds. |
| | | delay = Math.min(delay, diff); |
| | | } else if (pendingRequests.remove(future.getRequestID()) == null) { |
| | | } else if (pendingRequests.remove(promise.getRequestID()) == null) { |
| | | // Result arrived at the same time. |
| | | continue; |
| | | } else if (future.isBindOrStartTLS()) { |
| | | } else if (promise.isBindOrStartTLS()) { |
| | | /* |
| | | * No other operations can be performed while a bind or StartTLS |
| | | * request is active, so we cannot time out the request. We |
| | |
| | | * ignoring timeouts could cause the application to hang. |
| | | */ |
| | | logger.debug(LocalizableMessage.raw("Failing bind or StartTLS request due to timeout %s" |
| | | + "(connection will be invalidated): ", future)); |
| | | + "(connection will be invalidated): ", promise)); |
| | | final Result result = Responses.newResult(ResultCode.CLIENT_SIDE_TIMEOUT).setDiagnosticMessage( |
| | | LDAP_CONNECTION_BIND_OR_START_TLS_REQUEST_TIMEOUT.get(timeout).toString()); |
| | | future.adaptErrorResult(result); |
| | | promise.adaptErrorResult(result); |
| | | |
| | | // Fail the connection. |
| | | final Result errorResult = Responses.newResult(ResultCode.CLIENT_SIDE_TIMEOUT).setDiagnosticMessage( |
| | | LDAP_CONNECTION_BIND_OR_START_TLS_CONNECTION_TIMEOUT.get(timeout).toString()); |
| | | connectionErrorOccurred(errorResult); |
| | | } else { |
| | | logger.debug(LocalizableMessage.raw("Failing request due to timeout: %s", future)); |
| | | logger.debug(LocalizableMessage.raw("Failing request due to timeout: %s", promise)); |
| | | final Result result = Responses.newResult(ResultCode.CLIENT_SIDE_TIMEOUT).setDiagnosticMessage( |
| | | LDAP_CONNECTION_REQUEST_TIMEOUT.get(timeout).toString()); |
| | | future.adaptErrorResult(result); |
| | | promise.adaptErrorResult(result); |
| | | |
| | | /* |
| | | * FIXME: there's a potential race condition here if a bind or |
| | |
| | | * could hang the application. |
| | | */ |
| | | // if (!bindOrStartTLSInProgress.get()) { |
| | | // sendAbandonRequest(newAbandonRequest(future.getRequestID())); |
| | | // sendAbandonRequest(newAbandonRequest(promise.getRequestID())); |
| | | // } |
| | | } |
| | | } |
| | |
| | | |
| | | // First abort all outstanding requests. |
| | | for (final int requestID : pendingRequests.keySet()) { |
| | | final AbstractLDAPFutureResultImpl<?> future = pendingRequests.remove(requestID); |
| | | if (future != null) { |
| | | future.adaptErrorResult(connectionInvalidReason); |
| | | final ResultLdapPromiseImpl<?, ?> promise = pendingRequests.remove(requestID); |
| | | if (promise != null) { |
| | | promise.adaptErrorResult(connectionInvalidReason); |
| | | } |
| | | } |
| | | |
| | |
| | | if (notifyErrorOccurred) { |
| | | for (final ConnectionEventListener listener : tmpListeners) { |
| | | // Use the reason provided in the disconnect notification. |
| | | listener.handleConnectionError(isDisconnectNotification, newErrorResult(reason)); |
| | | listener.handleConnectionError(isDisconnectNotification, newLdapException(reason)); |
| | | } |
| | | } |
| | | if (notifyClose) { |
| | |
| | | } |
| | | } |
| | | |
| | | int continuePendingBindRequest(final LDAPBindFutureResultImpl future) throws LdapException { |
| | | int continuePendingBindRequest(final BindResultLdapPromiseImpl promise) throws LdapException { |
| | | final int newMsgID = nextMsgID.getAndIncrement(); |
| | | synchronized (stateLock) { |
| | | checkConnectionIsValid(); |
| | | pendingRequests.put(newMsgID, future); |
| | | pendingRequests.put(newMsgID, promise); |
| | | } |
| | | return newMsgID; |
| | | } |
| | |
| | | return factory.getLDAPOptions(); |
| | | } |
| | | |
| | | AbstractLDAPFutureResultImpl<?> getPendingRequest(final Integer messageID) { |
| | | ResultLdapPromiseImpl<?, ?> getPendingRequest(final Integer messageID) { |
| | | return pendingRequests.get(messageID); |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | AbstractLDAPFutureResultImpl<?> removePendingRequest(final Integer messageID) { |
| | | ResultLdapPromiseImpl<?, ?> removePendingRequest(final Integer messageID) { |
| | | return pendingRequests.remove(messageID); |
| | | } |
| | | |
| | |
| | | // FIXME: Is this the best result code? |
| | | final Result errorResult = Responses.newResult(ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e); |
| | | connectionErrorOccurred(errorResult); |
| | | return newErrorResult(errorResult); |
| | | return newLdapException(errorResult); |
| | | } |
| | | |
| | | private void checkBindOrStartTLSInProgress() throws LdapException { |
| | | if (bindOrStartTLSInProgress.get()) { |
| | | throw newErrorResult(ResultCode.OPERATIONS_ERROR, "Bind or Start TLS operation in progress"); |
| | | throw newLdapException(ResultCode.OPERATIONS_ERROR, "Bind or Start TLS operation in progress"); |
| | | } |
| | | } |
| | | |
| | |
| | | * this could be misinterpreted as a genuine authentication |
| | | * failure for subsequent bind requests. |
| | | */ |
| | | throw newErrorResult(ResultCode.CLIENT_SIDE_SERVER_DOWN, "Connection closed by server"); |
| | | throw newLdapException(ResultCode.CLIENT_SIDE_SERVER_DOWN, "Connection closed by server"); |
| | | } else { |
| | | throw newErrorResult(connectionInvalidReason); |
| | | throw newLdapException(connectionInvalidReason); |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | import org.forgerock.i18n.slf4j.LocalizedLogger; |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.FutureResultImpl; |
| | | import org.forgerock.opendj.ldap.LDAPOptions; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.TimeoutChecker; |
| | | import org.forgerock.opendj.ldap.TimeoutEventListener; |
| | |
| | | import org.forgerock.opendj.ldap.spi.LDAPConnectionFactoryImpl; |
| | | import org.forgerock.util.promise.FailureHandler; |
| | | import org.forgerock.util.promise.Promise; |
| | | import org.forgerock.util.promise.PromiseImpl; |
| | | import org.forgerock.util.promise.SuccessHandler; |
| | | import org.glassfish.grizzly.CompletionHandler; |
| | | import org.glassfish.grizzly.EmptyCompletionHandler; |
| | |
| | | private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); |
| | | |
| | | /** |
| | | * Adapts a Grizzly connection completion handler to an LDAP connection |
| | | * asynchronous future result. |
| | | * Adapts a Grizzly connection completion handler to an LDAP connection promise. |
| | | */ |
| | | @SuppressWarnings("rawtypes") |
| | | private final class CompletionHandlerAdapter implements |
| | | CompletionHandler<org.glassfish.grizzly.Connection>, TimeoutEventListener { |
| | | private final FutureResultImpl<Connection> future; |
| | | private final PromiseImpl<Connection, LdapException> promise; |
| | | private final long timeoutEndTime; |
| | | |
| | | private CompletionHandlerAdapter(final FutureResultImpl<Connection> future) { |
| | | this.future = future; |
| | | private CompletionHandlerAdapter(final PromiseImpl<Connection, LdapException> promise) { |
| | | this.promise = promise; |
| | | final long timeoutMS = getTimeout(); |
| | | this.timeoutEndTime = timeoutMS > 0 ? System.currentTimeMillis() + timeoutMS : 0; |
| | | timeoutChecker.get().addListener(this); |
| | |
| | | |
| | | // Start TLS or install SSL layer asynchronously. |
| | | |
| | | // Give up immediately if the future has been cancelled or timed out. |
| | | if (future.isDone()) { |
| | | // Give up immediately if the promise has been cancelled or timed out. |
| | | if (promise.isDone()) { |
| | | timeoutChecker.get().removeListener(this); |
| | | connection.close(); |
| | | return; |
| | |
| | | public void failed(final Throwable throwable) { |
| | | // Adapt and forward. |
| | | timeoutChecker.get().removeListener(this); |
| | | future.handleError(adaptConnectionException(throwable)); |
| | | promise.handleError(adaptConnectionException(throwable)); |
| | | releaseTransportAndTimeoutChecker(); |
| | | } |
| | | |
| | |
| | | if (t instanceof LdapException) { |
| | | return (LdapException) t; |
| | | } else { |
| | | return newErrorResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR, t.getMessage(), t); |
| | | return newLdapException(ResultCode.CLIENT_SIDE_CONNECT_ERROR, t.getMessage(), t); |
| | | } |
| | | } |
| | | |
| | | private void onFailure(final GrizzlyLDAPConnection connection, final Throwable t) { |
| | | // Abort connection attempt due to error. |
| | | timeoutChecker.get().removeListener(this); |
| | | future.handleError(adaptConnectionException(t)); |
| | | promise.handleError(adaptConnectionException(t)); |
| | | connection.close(); |
| | | } |
| | | |
| | | private void onSuccess(final GrizzlyLDAPConnection connection) { |
| | | timeoutChecker.get().removeListener(this); |
| | | if (!future.tryHandleResult(connection)) { |
| | | if (!promise.tryHandleResult(connection)) { |
| | | // The connection has been either cancelled or it has timed out. |
| | | connection.close(); |
| | | } |
| | |
| | | } else if (timeoutEndTime > currentTime) { |
| | | return timeoutEndTime - currentTime; |
| | | } else { |
| | | future.handleError(newErrorResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR, |
| | | promise.handleError(newLdapException(ResultCode.CLIENT_SIDE_CONNECT_ERROR, |
| | | LDAP_CONNECTION_CONNECT_TIMEOUT.get(getSocketAddress(), getTimeout()).toString())); |
| | | return 0; |
| | | } |
| | |
| | | try { |
| | | return getConnectionAsync().getOrThrow(); |
| | | } catch (final InterruptedException e) { |
| | | throw newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED, e); |
| | | throw newLdapException(ResultCode.CLIENT_SIDE_USER_CANCELLED, e); |
| | | } |
| | | } |
| | | |
| | |
| | | final SocketConnectorHandler connectorHandler = |
| | | TCPNIOConnectorHandler.builder(transport.get()).processor(defaultFilterChain) |
| | | .build(); |
| | | final FutureResultImpl<Connection> future = new FutureResultImpl<Connection>(); |
| | | connectorHandler.connect(getSocketAddress(), new CompletionHandlerAdapter(future)); |
| | | return future; |
| | | final PromiseImpl<Connection, LdapException> promise = PromiseImpl.create(); |
| | | connectorHandler.connect(getSocketAddress(), new CompletionHandlerAdapter(promise)); |
| | | return promise; |
| | | } |
| | | |
| | | @Override |
| | |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.requests.AddRequest; |
| | | import org.forgerock.opendj.ldap.requests.BindClient; |
| | | import org.forgerock.opendj.ldap.requests.CompareRequest; |
| | | import org.forgerock.opendj.ldap.requests.DeleteRequest; |
| | | import org.forgerock.opendj.ldap.requests.ExtendedRequest; |
| | | import org.forgerock.opendj.ldap.requests.GenericBindRequest; |
| | | import org.forgerock.opendj.ldap.requests.ModifyDNRequest; |
| | | import org.forgerock.opendj.ldap.requests.ModifyRequest; |
| | | import org.forgerock.opendj.ldap.requests.SearchRequest; |
| | | import org.forgerock.opendj.ldap.requests.StartTLSExtendedRequest; |
| | | import org.forgerock.opendj.ldap.responses.BindResult; |
| | | import org.forgerock.opendj.ldap.responses.CompareResult; |
| | |
| | | import org.forgerock.opendj.ldap.responses.Result; |
| | | import org.forgerock.opendj.ldap.responses.SearchResultEntry; |
| | | import org.forgerock.opendj.ldap.responses.SearchResultReference; |
| | | import org.forgerock.opendj.ldap.spi.AbstractLDAPFutureResultImpl; |
| | | import org.forgerock.opendj.ldap.spi.LDAPBindFutureResultImpl; |
| | | import org.forgerock.opendj.ldap.spi.LDAPCompareFutureResultImpl; |
| | | import org.forgerock.opendj.ldap.spi.LDAPExtendedFutureResultImpl; |
| | | import org.forgerock.opendj.ldap.spi.LDAPFutureResultImpl; |
| | | import org.forgerock.opendj.ldap.spi.LDAPSearchFutureResultImpl; |
| | | import org.forgerock.opendj.ldap.spi.BindResultLdapPromiseImpl; |
| | | import org.forgerock.opendj.ldap.spi.ExtendedResultLdapPromiseImpl; |
| | | import org.forgerock.opendj.ldap.spi.ResultLdapPromiseImpl; |
| | | import org.forgerock.opendj.ldap.spi.SearchResultLdapPromiseImpl; |
| | | import org.glassfish.grizzly.Connection; |
| | | import org.glassfish.grizzly.EmptyCompletionHandler; |
| | | import org.glassfish.grizzly.Grizzly; |
| | |
| | | import org.glassfish.grizzly.filterchain.FilterChainContext; |
| | | import org.glassfish.grizzly.filterchain.NextAction; |
| | | |
| | | import static org.forgerock.opendj.ldap.ResultCode.*; |
| | | import static org.forgerock.opendj.ldap.responses.Responses.*; |
| | | |
| | | /** |
| | | * Grizzly filter implementation for decoding LDAP responses and handling client |
| | | * side logic for SSL and SASL operations over LDAP. |
| | |
| | | return this.reader; |
| | | } |
| | | |
| | | @SuppressWarnings({ "rawtypes", "unchecked" }) |
| | | @Override |
| | | public void addResult(final int messageID, final Result result) throws DecodeException, |
| | | IOException { |
| | | final GrizzlyLDAPConnection ldapConnection = |
| | | LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | if (ldapConnection != null) { |
| | | final AbstractLDAPFutureResultImpl<?> pendingRequest = |
| | | ldapConnection.removePendingRequest(messageID); |
| | | |
| | | final ResultLdapPromiseImpl pendingRequest = ldapConnection.removePendingRequest(messageID); |
| | | if (pendingRequest != null) { |
| | | if (pendingRequest instanceof LDAPFutureResultImpl) { |
| | | final LDAPFutureResultImpl future = (LDAPFutureResultImpl) pendingRequest; |
| | | if (future.getRequest() instanceof AddRequest) { |
| | | future.setResultOrError(result); |
| | | return; |
| | | } |
| | | if (pendingRequest.getRequest() instanceof AddRequest) { |
| | | pendingRequest.setResultOrError(result); |
| | | return; |
| | | } |
| | | throw newUnexpectedResponseException(messageID, result); |
| | | } |
| | |
| | | @Override |
| | | public void bindResult(final int messageID, final BindResult result) |
| | | throws DecodeException, IOException { |
| | | final GrizzlyLDAPConnection ldapConnection = |
| | | LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | if (ldapConnection != null) { |
| | | final AbstractLDAPFutureResultImpl<?> pendingRequest = |
| | | ldapConnection.removePendingRequest(messageID); |
| | | |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingRequest(messageID); |
| | | if (pendingRequest != null) { |
| | | if (pendingRequest instanceof LDAPBindFutureResultImpl) { |
| | | final LDAPBindFutureResultImpl future = |
| | | ((LDAPBindFutureResultImpl) pendingRequest); |
| | | final BindClient bindClient = future.getBindClient(); |
| | | if (pendingRequest instanceof BindResultLdapPromiseImpl) { |
| | | final BindResultLdapPromiseImpl promise = (BindResultLdapPromiseImpl) pendingRequest; |
| | | final BindClient bindClient = promise.getBindClient(); |
| | | |
| | | try { |
| | | if (!bindClient.evaluateResult(result)) { |
| | | // The server is expecting a multi stage |
| | | // bind response. |
| | | final int msgID = ldapConnection.continuePendingBindRequest(future); |
| | | final int msgID = ldapConnection.continuePendingBindRequest(promise); |
| | | |
| | | LDAPWriter<ASN1BufferWriter> ldapWriter = GrizzlyUtils.getWriter(); |
| | | try { |
| | |
| | | } |
| | | } catch (final LdapException e) { |
| | | ldapConnection.setBindOrStartTLSInProgress(false); |
| | | future.adaptErrorResult(e.getResult()); |
| | | promise.adaptErrorResult(e.getResult()); |
| | | return; |
| | | } catch (final IOException e) { |
| | | // FIXME: I18N need to have a better error |
| | | // message. |
| | | // FIXME: I18N need to have a better error message. |
| | | // FIXME: Is this the best result code? |
| | | ldapConnection.setBindOrStartTLSInProgress(false); |
| | | final Result errorResult = |
| | |
| | | .setDiagnosticMessage( |
| | | "An error occurred during multi-stage authentication") |
| | | .setCause(e); |
| | | future.adaptErrorResult(errorResult); |
| | | promise.adaptErrorResult(errorResult); |
| | | return; |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | ldapConnection.setBindOrStartTLSInProgress(false); |
| | | future.setResultOrError(result); |
| | | promise.setResultOrError(result); |
| | | return; |
| | | } |
| | | throw newUnexpectedResponseException(messageID, result); |
| | |
| | | } |
| | | } |
| | | |
| | | @SuppressWarnings("unchecked") |
| | | @Override |
| | | public void compareResult(final int messageID, final CompareResult result) |
| | | throws DecodeException, IOException { |
| | | final GrizzlyLDAPConnection ldapConnection = |
| | | LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | if (ldapConnection != null) { |
| | | final AbstractLDAPFutureResultImpl<?> pendingRequest = |
| | | ldapConnection.removePendingRequest(messageID); |
| | | |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingRequest(messageID); |
| | | if (pendingRequest != null) { |
| | | if (pendingRequest instanceof LDAPCompareFutureResultImpl) { |
| | | final LDAPCompareFutureResultImpl future = |
| | | (LDAPCompareFutureResultImpl) pendingRequest; |
| | | future.setResultOrError(result); |
| | | if (pendingRequest.getRequest() instanceof CompareRequest) { |
| | | ((ResultLdapPromiseImpl<CompareRequest, CompareResult>) pendingRequest) |
| | | .setResultOrError(result); |
| | | return; |
| | | } |
| | | throw newUnexpectedResponseException(messageID, result); |
| | |
| | | } |
| | | } |
| | | |
| | | @SuppressWarnings("unchecked") |
| | | @Override |
| | | public void deleteResult(final int messageID, final Result result) throws DecodeException, |
| | | IOException { |
| | | final GrizzlyLDAPConnection ldapConnection = |
| | | LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | if (ldapConnection != null) { |
| | | final AbstractLDAPFutureResultImpl<?> pendingRequest = |
| | | ldapConnection.removePendingRequest(messageID); |
| | | |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingRequest(messageID); |
| | | if (pendingRequest != null) { |
| | | if (pendingRequest instanceof LDAPFutureResultImpl) { |
| | | final LDAPFutureResultImpl future = (LDAPFutureResultImpl) pendingRequest; |
| | | if (future.getRequest() instanceof DeleteRequest) { |
| | | future.setResultOrError(result); |
| | | return; |
| | | } |
| | | if (pendingRequest.getRequest() instanceof DeleteRequest) { |
| | | ((ResultLdapPromiseImpl<DeleteRequest, Result>) pendingRequest).setResultOrError(result); |
| | | return; |
| | | } |
| | | throw newUnexpectedResponseException(messageID, result); |
| | | } |
| | |
| | | @Override |
| | | public void extendedResult(final int messageID, final ExtendedResult result) |
| | | throws DecodeException, IOException { |
| | | final GrizzlyLDAPConnection ldapConnection = |
| | | LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | if (ldapConnection != null) { |
| | | if (messageID == 0) { |
| | | // Unsolicited notification received. |
| | | if ((result.getOID() != null) |
| | | && result.getOID().equals(LDAP.OID_NOTICE_OF_DISCONNECTION)) { |
| | | if (result.getOID() != null && result.getOID().equals(LDAP.OID_NOTICE_OF_DISCONNECTION)) { |
| | | // Treat this as a connection error. |
| | | final Result errorResult = |
| | | Responses.newResult(result.getResultCode()).setDiagnosticMessage( |
| | | final Result errorResult = newResult(result.getResultCode()).setDiagnosticMessage( |
| | | result.getDiagnosticMessage()); |
| | | ldapConnection.close(null, true, errorResult); |
| | | } else { |
| | | ldapConnection.handleUnsolicitedNotification(result); |
| | | } |
| | | } else { |
| | | final AbstractLDAPFutureResultImpl<?> pendingRequest = |
| | | ldapConnection.removePendingRequest(messageID); |
| | | |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingRequest(messageID); |
| | | if (pendingRequest != null) { |
| | | if (pendingRequest instanceof LDAPExtendedFutureResultImpl<?>) { |
| | | final LDAPExtendedFutureResultImpl<?> extendedFuture = |
| | | ((LDAPExtendedFutureResultImpl<?>) pendingRequest); |
| | | if (pendingRequest.getRequest() instanceof ExtendedRequest) { |
| | | final ExtendedResultLdapPromiseImpl<?> extendedPromise = |
| | | (ExtendedResultLdapPromiseImpl<?>) pendingRequest; |
| | | try { |
| | | handleExtendedResult0(ldapConnection, extendedFuture, result); |
| | | handleExtendedResult0(ldapConnection, extendedPromise, result); |
| | | } catch (final DecodeException de) { |
| | | // FIXME: should the connection be closed as |
| | | // well? |
| | | final Result errorResult = |
| | | Responses.newResult(ResultCode.CLIENT_SIDE_DECODING_ERROR) |
| | | // FIXME: should the connection be closed as well? |
| | | final Result errorResult = newResult(ResultCode.CLIENT_SIDE_DECODING_ERROR) |
| | | .setDiagnosticMessage(de.getLocalizedMessage()) |
| | | .setCause(de); |
| | | extendedFuture.adaptErrorResult(errorResult); |
| | | extendedPromise.adaptErrorResult(errorResult); |
| | | } |
| | | } else { |
| | | throw newUnexpectedResponseException(messageID, result); |
| | |
| | | @Override |
| | | public void intermediateResponse(final int messageID, final IntermediateResponse response) |
| | | throws DecodeException, IOException { |
| | | final GrizzlyLDAPConnection ldapConnection = |
| | | LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | if (ldapConnection != null) { |
| | | final AbstractLDAPFutureResultImpl<?> pendingRequest = |
| | | ldapConnection.getPendingRequest(messageID); |
| | | |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.getPendingRequest(messageID); |
| | | if (pendingRequest != null) { |
| | | pendingRequest.handleIntermediateResponse(response); |
| | | } |
| | | } |
| | | } |
| | | |
| | | @SuppressWarnings("unchecked") |
| | | @Override |
| | | public void modifyDNResult(final int messageID, final Result result) |
| | | throws DecodeException, IOException { |
| | | final GrizzlyLDAPConnection ldapConnection = |
| | | LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | if (ldapConnection != null) { |
| | | final AbstractLDAPFutureResultImpl<?> pendingRequest = |
| | | ldapConnection.removePendingRequest(messageID); |
| | | |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingRequest(messageID); |
| | | if (pendingRequest != null) { |
| | | if (pendingRequest instanceof LDAPFutureResultImpl) { |
| | | final LDAPFutureResultImpl future = (LDAPFutureResultImpl) pendingRequest; |
| | | if (future.getRequest() instanceof ModifyDNRequest) { |
| | | future.setResultOrError(result); |
| | | return; |
| | | } |
| | | if (pendingRequest.getRequest() instanceof ModifyDNRequest) { |
| | | ((ResultLdapPromiseImpl<ModifyDNRequest, Result>) pendingRequest).setResultOrError(result); |
| | | return; |
| | | } |
| | | throw newUnexpectedResponseException(messageID, result); |
| | | } |
| | | } |
| | | } |
| | | |
| | | @SuppressWarnings("unchecked") |
| | | @Override |
| | | public void modifyResult(final int messageID, final Result result) throws DecodeException, |
| | | IOException { |
| | | final GrizzlyLDAPConnection ldapConnection = |
| | | LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | public void modifyResult(final int messageID, final Result result) throws DecodeException, IOException { |
| | | final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | if (ldapConnection != null) { |
| | | final AbstractLDAPFutureResultImpl<?> pendingRequest = |
| | | ldapConnection.removePendingRequest(messageID); |
| | | |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingRequest(messageID); |
| | | if (pendingRequest != null) { |
| | | if (pendingRequest instanceof LDAPFutureResultImpl) { |
| | | final LDAPFutureResultImpl future = (LDAPFutureResultImpl) pendingRequest; |
| | | if (future.getRequest() instanceof ModifyRequest) { |
| | | future.setResultOrError(result); |
| | | return; |
| | | } |
| | | if (pendingRequest.getRequest() instanceof ModifyRequest) { |
| | | ((ResultLdapPromiseImpl<ModifyRequest, Result>) pendingRequest).setResultOrError(result); |
| | | return; |
| | | } |
| | | throw newUnexpectedResponseException(messageID, result); |
| | | } |
| | | } |
| | | } |
| | | |
| | | @SuppressWarnings("unchecked") |
| | | @Override |
| | | public void searchResult(final int messageID, final Result result) throws DecodeException, |
| | | IOException { |
| | | final GrizzlyLDAPConnection ldapConnection = |
| | | LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | if (ldapConnection != null) { |
| | | final AbstractLDAPFutureResultImpl<?> pendingRequest = |
| | | ldapConnection.removePendingRequest(messageID); |
| | | |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingRequest(messageID); |
| | | if (pendingRequest != null) { |
| | | if (pendingRequest instanceof LDAPSearchFutureResultImpl) { |
| | | ((LDAPSearchFutureResultImpl) pendingRequest).setResultOrError(result); |
| | | if (pendingRequest.getRequest() instanceof SearchRequest) { |
| | | ((ResultLdapPromiseImpl<SearchRequest, Result>) pendingRequest).setResultOrError(result); |
| | | } else { |
| | | throw newUnexpectedResponseException(messageID, result); |
| | | } |
| | |
| | | @Override |
| | | public void searchResultEntry(final int messageID, final SearchResultEntry entry) |
| | | throws DecodeException, IOException { |
| | | final GrizzlyLDAPConnection ldapConnection = |
| | | LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | if (ldapConnection != null) { |
| | | final AbstractLDAPFutureResultImpl<?> pendingRequest = |
| | | ldapConnection.getPendingRequest(messageID); |
| | | |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.getPendingRequest(messageID); |
| | | if (pendingRequest != null) { |
| | | if (pendingRequest instanceof LDAPSearchFutureResultImpl) { |
| | | ((LDAPSearchFutureResultImpl) pendingRequest).handleEntry(entry); |
| | | if (pendingRequest instanceof SearchResultLdapPromiseImpl) { |
| | | ((SearchResultLdapPromiseImpl) pendingRequest).handleEntry(entry); |
| | | } else { |
| | | throw newUnexpectedResponseException(messageID, entry); |
| | | } |
| | |
| | | @Override |
| | | public void searchResultReference(final int messageID, final SearchResultReference reference) |
| | | throws DecodeException, IOException { |
| | | final GrizzlyLDAPConnection ldapConnection = |
| | | LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | if (ldapConnection != null) { |
| | | final AbstractLDAPFutureResultImpl<?> pendingRequest = |
| | | ldapConnection.getPendingRequest(messageID); |
| | | |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.getPendingRequest(messageID); |
| | | if (pendingRequest != null) { |
| | | if (pendingRequest instanceof LDAPSearchFutureResultImpl) { |
| | | ((LDAPSearchFutureResultImpl) pendingRequest).handleReference(reference); |
| | | if (pendingRequest instanceof SearchResultLdapPromiseImpl) { |
| | | ((SearchResultLdapPromiseImpl) pendingRequest).handleReference(reference); |
| | | } else { |
| | | throw newUnexpectedResponseException(messageID, reference); |
| | | } |
| | |
| | | |
| | | // Needed in order to expose type information. |
| | | private <R extends ExtendedResult> void handleExtendedResult0( |
| | | final GrizzlyLDAPConnection conn, final LDAPExtendedFutureResultImpl<R> future, |
| | | final GrizzlyLDAPConnection conn, final ExtendedResultLdapPromiseImpl<R> promise, |
| | | final ExtendedResult result) throws DecodeException { |
| | | final R decodedResponse = |
| | | future.decodeResult(result, conn.getLDAPOptions().getDecodeOptions()); |
| | | final R decodedResponse = promise.decodeResult(result, conn.getLDAPOptions().getDecodeOptions()); |
| | | |
| | | if (future.getRequest() instanceof StartTLSExtendedRequest) { |
| | | if (promise.getRequest() instanceof StartTLSExtendedRequest) { |
| | | if (result.getResultCode() == ResultCode.SUCCESS) { |
| | | try { |
| | | final StartTLSExtendedRequest request = |
| | | (StartTLSExtendedRequest) future.getRequest(); |
| | | final StartTLSExtendedRequest request = (StartTLSExtendedRequest) promise.getRequest(); |
| | | conn.startTLS(request.getSSLContext(), request.getEnabledProtocols(), |
| | | request.getEnabledCipherSuites(), |
| | | new EmptyCompletionHandler<SSLEngine>() { |
| | | @Override |
| | | public void completed(final SSLEngine result) { |
| | | conn.setBindOrStartTLSInProgress(false); |
| | | future.setResultOrError(decodedResponse); |
| | | promise.setResultOrError(decodedResponse); |
| | | } |
| | | |
| | | @Override |
| | | public void failed(final Throwable throwable) { |
| | | final Result errorResult = |
| | | Responses.newResult( |
| | | ResultCode.CLIENT_SIDE_LOCAL_ERROR) |
| | | .setCause(throwable).setDiagnosticMessage( |
| | | "SSL handshake failed"); |
| | | final Result errorResult = newResult(CLIENT_SIDE_LOCAL_ERROR) |
| | | .setCause(throwable).setDiagnosticMessage("SSL handshake failed"); |
| | | conn.setBindOrStartTLSInProgress(false); |
| | | conn.close(null, false, errorResult); |
| | | future.adaptErrorResult(errorResult); |
| | | promise.adaptErrorResult(errorResult); |
| | | } |
| | | }); |
| | | return; |
| | | } catch (final IOException e) { |
| | | final Result errorResult = |
| | | Responses.newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR).setCause(e) |
| | | .setDiagnosticMessage(e.getMessage()); |
| | | future.adaptErrorResult(errorResult); |
| | | final Result errorResult = newResult(CLIENT_SIDE_LOCAL_ERROR).setCause(e) |
| | | .setDiagnosticMessage(e.getMessage()); |
| | | promise.adaptErrorResult(errorResult); |
| | | conn.close(null, false, errorResult); |
| | | return; |
| | | } |
| | | } |
| | | } |
| | | |
| | | future.setResultOrError(decodedResponse); |
| | | promise.setResultOrError(decodedResponse); |
| | | } |
| | | } |
| | | |
| | |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.FailoverLoadBalancingAlgorithm; |
| | | import org.forgerock.opendj.ldap.FutureResult; |
| | | import org.forgerock.opendj.ldap.LdapPromise; |
| | | import org.forgerock.opendj.ldap.IntermediateResponseHandler; |
| | | import org.forgerock.opendj.ldap.LDAPClientContext; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | |
| | | import static org.fest.assertions.Assertions.*; |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | import static org.forgerock.opendj.ldap.LdapException.*; |
| | | import static org.forgerock.opendj.ldap.FutureResultWrapper.*; |
| | | import static org.forgerock.opendj.ldap.TestCaseUtils.*; |
| | | import static org.forgerock.opendj.ldap.spi.LdapPromises.*; |
| | | import static org.mockito.Matchers.*; |
| | | import static org.mockito.Mockito.*; |
| | | import static org.testng.Assert.*; |
| | |
| | | } |
| | | |
| | | /** |
| | | * Tests the async connection in the blocking mode. This is not fully async |
| | | * as it blocks on the future. |
| | | * Tests the async connection in the blocking mode. This is not fully async as it blocks on the promise. |
| | | * |
| | | * @throws Exception |
| | | */ |
| | | @Test(dataProvider = "connectionFactories", timeOut = TEST_TIMEOUT_MS) |
| | | public void testBlockingFutureNoHandler(ConnectionFactory factory) throws Exception { |
| | | public void testBlockingPromiseNoHandler(ConnectionFactory factory) throws Exception { |
| | | final Promise<? extends Connection, LdapException> promise = factory.getConnectionAsync(); |
| | | final Connection con = promise.get(); |
| | | // quickly check if it is a valid connection. |
| | |
| | | * @throws Exception |
| | | */ |
| | | @Test(dataProvider = "connectionFactories", timeOut = TEST_TIMEOUT_MS) |
| | | public void testNonBlockingFutureWithHandler(ConnectionFactory factory) throws Exception { |
| | | // Use the handler to get the result asynchronously. |
| | | public void testNonBlockingPromiseWithHandler(ConnectionFactory factory) throws Exception { |
| | | // Use the promise to get the result asynchronously. |
| | | final PromiseImpl<Connection, LdapException> promise = PromiseImpl.create(); |
| | | |
| | | factory.getConnectionAsync().onSuccess(new SuccessHandler<Connection>() { |
| | |
| | | promise.handleResult(con); |
| | | } |
| | | }).onFailure(new FailureHandler<LdapException>() { |
| | | |
| | | @Override |
| | | public void handleError(LdapException error) { |
| | | promise.handleError(error); |
| | |
| | | |
| | | // Since we don't have anything to do, we would rather |
| | | // be notified by the promise when the other thread calls our handler. |
| | | promise.getOrThrow(); // should do a timed wait rather? |
| | | // should do a timed wait rather? |
| | | promise.getOrThrow(); |
| | | } |
| | | |
| | | /** |
| | |
| | | |
| | | // Mock underlying connection factory which always succeeds. |
| | | final ConnectionFactory mockFactory = mock(ConnectionFactory.class); |
| | | when(mockFactory.getConnectionAsync()).thenAnswer(new Answer<FutureResult<Connection>>() { |
| | | when(mockFactory.getConnectionAsync()).thenAnswer(new Answer<LdapPromise<Connection>>() { |
| | | |
| | | @Override |
| | | public FutureResult<Connection> answer(InvocationOnMock invocation) throws Throwable { |
| | | public LdapPromise<Connection> answer(InvocationOnMock invocation) throws Throwable { |
| | | // Update state. |
| | | final int connectionID = realConnectionCount.getAndIncrement(); |
| | | realConnectionIsClosed[connectionID] = false; |
| | |
| | | when(mockConnection.isValid()).thenReturn(true); |
| | | when(mockConnection.toString()).thenReturn("Mock connection " + connectionID); |
| | | |
| | | return newSuccessfulFutureResult(mockConnection); |
| | | return newSuccessfulLdapPromise(mockConnection); |
| | | } |
| | | }); |
| | | |
| | |
| | | contextHolder.set((LDAPClientContext) invocation.getArguments()[0]); |
| | | connectLatch.countDown(); /* is this needed? */ |
| | | if (config.closeOnAccept) { |
| | | throw newErrorResult(ResultCode.UNAVAILABLE); |
| | | throw newLdapException(ResultCode.UNAVAILABLE); |
| | | } else { |
| | | // Return a mock connection which always succeeds for binds. |
| | | ServerConnection<Integer> mockConnection = mock(ServerConnection.class); |
| | |
| | | import org.forgerock.opendj.ldap.Connections; |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.FutureResult; |
| | | import org.forgerock.opendj.ldap.LdapPromise; |
| | | import org.forgerock.opendj.ldap.IntermediateResponseHandler; |
| | | import org.forgerock.opendj.ldap.LDAPClientContext; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | |
| | | try { |
| | | for (int i = 0; i < ITERATIONS; i++) { |
| | | final PromiseImpl<LdapException, NeverThrowsException> promise = PromiseImpl.create(); |
| | | final Promise<? extends Connection, LdapException> future = factory.getConnectionAsync(); |
| | | future.onFailure(getFailureHandler(promise)); |
| | | final Promise<? extends Connection, LdapException> connectionPromise = factory.getConnectionAsync(); |
| | | connectionPromise.onFailure(getFailureHandler(promise)); |
| | | |
| | | ConnectionException e = (ConnectionException) promise.getOrThrow(TEST_TIMEOUT, TimeUnit.SECONDS); |
| | | assertThat(e.getResult().getResultCode()).isEqualTo(ResultCode.CLIENT_SIDE_CONNECT_ERROR); |
| | | // Wait for the connect to timeout. |
| | | try { |
| | | future.getOrThrow(TEST_TIMEOUT, TimeUnit.SECONDS); |
| | | connectionPromise.getOrThrow(TEST_TIMEOUT, TimeUnit.SECONDS); |
| | | fail("The connect request succeeded unexpectedly"); |
| | | } catch (ConnectionException ce) { |
| | | assertThat(ce.getResult().getResultCode()).isEqualTo(ResultCode.CLIENT_SIDE_CONNECT_ERROR); |
| | |
| | | final MockConnectionEventListener listener = new MockConnectionEventListener(); |
| | | connection.addConnectionEventListener(listener); |
| | | final PromiseImpl<LdapException, NeverThrowsException> promise = PromiseImpl.create(); |
| | | final FutureResult<BindResult> future = connection.bindAsync(newSimpleBindRequest()); |
| | | future.onFailure(getFailureHandler(promise)); |
| | | final LdapPromise<BindResult> bindPromise = connection.bindAsync(newSimpleBindRequest()); |
| | | bindPromise.onFailure(getFailureHandler(promise)); |
| | | waitForBind(); |
| | | |
| | | TimeoutResultException e = (TimeoutResultException) promise.getOrThrow(TEST_TIMEOUT, TimeUnit.SECONDS); |
| | |
| | | |
| | | // Wait for the request to timeout. |
| | | try { |
| | | future.getOrThrow(TEST_TIMEOUT, TimeUnit.SECONDS); |
| | | bindPromise.getOrThrow(TEST_TIMEOUT, TimeUnit.SECONDS); |
| | | fail("The bind request succeeded unexpectedly"); |
| | | } catch (TimeoutResultException te) { |
| | | verifyResultCodeIsClientSideTimeout(te); |
| | |
| | | |
| | | // Now bind with timeout. |
| | | final PromiseImpl<LdapException, NeverThrowsException> promise = PromiseImpl.create(); |
| | | final FutureResult<BindResult> future = connection.bindAsync(newSimpleBindRequest()); |
| | | future.onFailure(getFailureHandler(promise)); |
| | | final LdapPromise<BindResult> bindPromise = connection.bindAsync(newSimpleBindRequest()); |
| | | bindPromise.onFailure(getFailureHandler(promise)); |
| | | waitForBind(); |
| | | |
| | | // Wait for the request to timeout and check the handler was invoked. |
| | | TimeoutResultException e = (TimeoutResultException) promise.getOrThrow(5, TimeUnit.SECONDS); |
| | | verifyResultCodeIsClientSideTimeout(e); |
| | | |
| | | // Now check the future was completed as expected. |
| | | // Now check the promise was completed as expected. |
| | | try { |
| | | future.getOrThrow(5, TimeUnit.SECONDS); |
| | | bindPromise.getOrThrow(5, TimeUnit.SECONDS); |
| | | fail("The bind request succeeded unexpectedly"); |
| | | } catch (TimeoutResultException te) { |
| | | verifyResultCodeIsClientSideTimeout(te); |
| | |
| | | final ConnectionEventListener listener = mock(ConnectionEventListener.class); |
| | | connection.addConnectionEventListener(listener); |
| | | final PromiseImpl<LdapException, NeverThrowsException> promise = PromiseImpl.create(); |
| | | final FutureResult<SearchResultEntry> future = connection.readEntryAsync(DN.valueOf("cn=test"), null); |
| | | future.onFailure(getFailureHandler(promise)); |
| | | final LdapPromise<SearchResultEntry> connectionPromise = |
| | | connection.readEntryAsync(DN.valueOf("cn=test"), null); |
| | | connectionPromise.onFailure(getFailureHandler(promise)); |
| | | waitForSearch(); |
| | | |
| | | LdapException e = promise.getOrThrow(TEST_TIMEOUT, TimeUnit.SECONDS); |
| | | verifyResultCodeIsClientSideTimeout(e); |
| | | // Wait for the request to timeout. |
| | | try { |
| | | future.getOrThrow(TEST_TIMEOUT, TimeUnit.SECONDS); |
| | | connectionPromise.getOrThrow(TEST_TIMEOUT, TimeUnit.SECONDS); |
| | | fail("The search request succeeded unexpectedly"); |
| | | } catch (TimeoutResultException te) { |
| | | verifyResultCodeIsClientSideTimeout(te); |
| | |
| | | import org.forgerock.opendj.ldap.ConnectionFactory; |
| | | import org.forgerock.opendj.ldap.Connections; |
| | | import org.forgerock.opendj.ldap.DecodeException; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.FutureResultImpl; |
| | | import org.forgerock.opendj.ldap.IntermediateResponseHandler; |
| | | import org.forgerock.opendj.ldap.LDAPClientContext; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LDAPListener; |
| | | import org.forgerock.opendj.ldap.LDAPListenerOptions; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.ProviderNotFoundException; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.ResultHandler; |
| | |
| | | import org.forgerock.opendj.ldap.responses.ExtendedResult; |
| | | import org.forgerock.opendj.ldap.responses.Responses; |
| | | import org.forgerock.opendj.ldap.responses.Result; |
| | | import org.forgerock.util.promise.PromiseImpl; |
| | | import org.testng.annotations.AfterClass; |
| | | import org.testng.annotations.BeforeClass; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.fest.assertions.Assertions.assertThat; |
| | | import static org.fest.assertions.Fail.fail; |
| | | import static org.forgerock.opendj.ldap.TestCaseUtils.findFreeSocketAddress; |
| | | import static org.mockito.Mockito.mock; |
| | | import static org.fest.assertions.Assertions.*; |
| | | import static org.fest.assertions.Fail.*; |
| | | import static org.forgerock.opendj.ldap.LdapException.*; |
| | | import static org.forgerock.opendj.ldap.TestCaseUtils.*; |
| | | import static org.mockito.Mockito.*; |
| | | |
| | | /** |
| | | * Tests the LDAPListener class. |
| | |
| | | public class GrizzlyLDAPListenerTestCase extends SdkTestCase { |
| | | |
| | | private static class MockServerConnection implements ServerConnection<Integer> { |
| | | final FutureResultImpl<Throwable> connectionError = new FutureResultImpl<Throwable>(); |
| | | final FutureResultImpl<LDAPClientContext> context = new FutureResultImpl<LDAPClientContext>(); |
| | | final PromiseImpl<Throwable, LdapException> connectionError = PromiseImpl.create(); |
| | | final PromiseImpl<LDAPClientContext, LdapException> context = PromiseImpl.create(); |
| | | final CountDownLatch isClosed = new CountDownLatch(1); |
| | | |
| | | MockServerConnection() { |
| | |
| | | final ExtendedRequest<R> request, |
| | | final IntermediateResponseHandler intermediateResponseHandler, |
| | | final ResultHandler<R> resultHandler) throws UnsupportedOperationException { |
| | | resultHandler.handleError(LdapException.newErrorResult(request |
| | | resultHandler.handleError(newLdapException(request |
| | | .getResultDecoder().newExtendedErrorResult(ResultCode.PROTOCOL_ERROR, "", |
| | | "Extended operation " + request.getOID() + " not supported"))); |
| | | } |
| | |
| | | resultHandler.handleResult(Responses.newBindResult(ResultCode.SUCCESS)); |
| | | } catch (final Exception e) { |
| | | // Unexpected. |
| | | resultHandler.handleError(LdapException.newErrorResult( |
| | | ResultCode.OTHER, |
| | | resultHandler.handleError(newLdapException(ResultCode.OTHER, |
| | | "Unexpected exception when connecting to load balancer", e)); |
| | | } |
| | | } |
| | |
| | | try { |
| | | // This is expected to fail. |
| | | lcf.getConnection().close(); |
| | | throw LdapException.newErrorResult(ResultCode.OTHER, |
| | | throw newLdapException(ResultCode.OTHER, |
| | | "Connection to offline server succeeded unexpectedly"); |
| | | } catch (final ConnectionException ce) { |
| | | // This is expected - so go to online server. |
| | |
| | | lcf.getConnection().close(); |
| | | } catch (final Exception e) { |
| | | // Unexpected. |
| | | throw LdapException.newErrorResult( |
| | | throw newLdapException( |
| | | ResultCode.OTHER, |
| | | "Unexpected exception when connecting to online server", |
| | | e); |
| | | } |
| | | } catch (final Exception e) { |
| | | // Unexpected. |
| | | throw LdapException.newErrorResult( |
| | | throw newLdapException( |
| | | ResultCode.OTHER, |
| | | "Unexpected exception when connecting to offline server", |
| | | e); |
| | |
| | | try { |
| | | // This is expected to fail. |
| | | lcf.getConnection().close(); |
| | | resultHandler.handleError(LdapException.newErrorResult( |
| | | resultHandler.handleError(newLdapException( |
| | | ResultCode.OTHER, |
| | | "Connection to offline server succeeded unexpectedly")); |
| | | } catch (final ConnectionException ce) { |
| | |
| | | resultHandler.handleResult(Responses.newBindResult(ResultCode.SUCCESS)); |
| | | } catch (final Exception e) { |
| | | // Unexpected. |
| | | resultHandler.handleError(LdapException.newErrorResult( |
| | | resultHandler.handleError(newLdapException( |
| | | ResultCode.OTHER, |
| | | "Unexpected exception when connecting to online server", e)); |
| | | } |
| | | } catch (final Exception e) { |
| | | // Unexpected. |
| | | resultHandler.handleError(LdapException.newErrorResult( |
| | | resultHandler.handleError(newLdapException( |
| | | ResultCode.OTHER, |
| | | "Unexpected exception when connecting to offline server", e)); |
| | | } |
| | |
| | | |
| | | if (request.getAuthenticationType() != BindRequest.AUTHENTICATION_TYPE_SIMPLE) { |
| | | // TODO: SASL authentication not implemented. |
| | | resultHandler.handleError(newErrorResult(ResultCode.PROTOCOL_ERROR, |
| | | resultHandler.handleError(newLdapException(ResultCode.PROTOCOL_ERROR, |
| | | "non-SIMPLE authentication not supported: " + request.getAuthenticationType())); |
| | | } else { |
| | | // Authenticate using a separate bind connection pool, because |
| | |
| | | final ResultHandler<R> resultHandler) { |
| | | if (CancelExtendedRequest.OID.equals(request.getOID())) { |
| | | // TODO: not implemented. |
| | | resultHandler.handleError(newErrorResult(ResultCode.PROTOCOL_ERROR, |
| | | resultHandler.handleError(newLdapException(ResultCode.PROTOCOL_ERROR, |
| | | "Cancel extended request operation not supported")); |
| | | } else if (StartTLSExtendedRequest.OID.equals(request.getOID())) { |
| | | // TODO: not implemented. |
| | | resultHandler.handleError(newErrorResult(ResultCode.PROTOCOL_ERROR, |
| | | resultHandler.handleError(newLdapException(ResultCode.PROTOCOL_ERROR, |
| | | "StartTLS extended request operation not supported")); |
| | | } else { |
| | | // Forward all other extended operations. |
| | |
| | | import java.util.concurrent.CountDownLatch; |
| | | |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.FutureResult; |
| | | import org.forgerock.opendj.ldap.FutureResultWrapper; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LdapPromise; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.SearchResultHandler; |
| | | import org.forgerock.opendj.ldap.SearchScope; |
| | |
| | | import org.forgerock.util.promise.Promise; |
| | | import org.forgerock.util.promise.SuccessHandler; |
| | | |
| | | import static org.forgerock.opendj.ldap.spi.LdapPromises.*; |
| | | |
| | | /** |
| | | * An example client application which searches a Directory Server using the |
| | | * asynchronous APIs. This example takes the following command line parameters: |
| | |
| | | @Override |
| | | public void handleError(LdapException error) { |
| | | System.err.println("Cancel request failed with result code: " |
| | | + error.getResult().getResultCode().intValue()); |
| | | + error.getResult().getResultCode().intValue()); |
| | | CANCEL_LATCH.countDown(); |
| | | } |
| | | }); |
| | |
| | | } |
| | | |
| | | } |
| | | // --- JCite search result handler --- |
| | | |
| | | // --- JCite search result handler --- |
| | | |
| | | // --- JCite decl1 --- |
| | | private static final CountDownLatch COMPLETION_LATCH = new CountDownLatch(1); |
| | |
| | | // --- JCite decl2 --- |
| | | static int requestID; |
| | | static int entryCount = 0; |
| | | |
| | | // --- JCite decl2 --- |
| | | |
| | | /** |
| | |
| | | return; |
| | | } |
| | | |
| | | |
| | | // Initiate the asynchronous connect, bind, and search. |
| | | final LDAPConnectionFactory factory = new LDAPConnectionFactory(hostName, port); |
| | | |
| | |
| | | }).thenAsync(new AsyncFunction<BindResult, Result, LdapException>() { |
| | | @Override |
| | | public Promise<Result, LdapException> apply(BindResult result) throws LdapException { |
| | | FutureResult<Result> future = FutureResultWrapper.asFutureResult(connection.searchAsync( |
| | | Requests.newSearchRequest(baseDN, scope, filter, attributes), new SearchResultHandlerImpl())); |
| | | requestID = future.getRequestID(); |
| | | return future; |
| | | LdapPromise<Result> promise = connection.searchAsync( |
| | | Requests.newSearchRequest(baseDN, scope, filter, attributes), new SearchResultHandlerImpl()); |
| | | requestID = promise.getRequestID(); |
| | | return promise; |
| | | } |
| | | }).onSuccess(new SuccessHandler<Result>() { |
| | | @Override |
| | |
| | | import com.forgerock.opendj.cli.MultiChoiceArgument; |
| | | import com.forgerock.opendj.cli.StringArgument; |
| | | |
| | | import static org.forgerock.opendj.ldap.LdapException.*; |
| | | |
| | | import static com.forgerock.opendj.cli.ArgumentConstants.*; |
| | | import static com.forgerock.opendj.cli.Utils.*; |
| | | import static com.forgerock.opendj.ldap.tools.ToolsMessages.*; |
| | |
| | | } catch (IOException e) { |
| | | // faking an error result by notifying the Handler |
| | | UpdateStatsResultHandler<Result> resHandler = new UpdateStatsResultHandler<Result>(currentTime); |
| | | resHandler.handleError(LdapException.newErrorResult(ResultCode.OTHER, e)); |
| | | resHandler.handleError(newLdapException(ResultCode.OTHER, e)); |
| | | return null; |
| | | } |
| | | } |
| | |
| | | */ |
| | | if (error instanceof EntryNotFoundException |
| | | || error instanceof MultipleEntriesFoundException) { |
| | | normalizedError = newErrorResult(ResultCode.INVALID_CREDENTIALS, error); |
| | | normalizedError = newLdapException(ResultCode.INVALID_CREDENTIALS, error); |
| | | } else if (error instanceof AuthenticationException |
| | | || error instanceof AuthorizationException) { |
| | | normalizedError = newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR, error); |
| | | normalizedError = newLdapException(ResultCode.CLIENT_SIDE_LOCAL_ERROR, error); |
| | | } else { |
| | | normalizedError = error; |
| | | } |
| | |
| | | import org.forgerock.opendj.ldap.ConnectionEventListener; |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.FutureResult; |
| | | import org.forgerock.opendj.ldap.LdapPromise; |
| | | import org.forgerock.opendj.ldap.IntermediateResponseHandler; |
| | | import org.forgerock.opendj.ldap.ResultHandler; |
| | | import org.forgerock.opendj.ldap.SearchResultHandler; |
| | |
| | | private static final class CachedRead implements SearchResultHandler, ResultHandler<Result> { |
| | | private SearchResultEntry cachedEntry; |
| | | private final String cachedFilterString; |
| | | private FutureResult<Result> cachedFuture; // Guarded by latch. |
| | | private final CountDownLatch cachedFutureLatch = new CountDownLatch(1); |
| | | /** Guarded by cachedPromiseLatch.*/ |
| | | private LdapPromise<Result> cachedPromise; |
| | | private final CountDownLatch cachedPromiseLatch = new CountDownLatch(1); |
| | | private final SearchRequest cachedRequest; |
| | | private volatile Result cachedResult; |
| | | private final ConcurrentLinkedQueue<SearchResultHandler> waitingResultHandlers = |
| | |
| | | } |
| | | } |
| | | |
| | | FutureResult<Result> getFutureResult() { |
| | | LdapPromise<Result> getPromise() { |
| | | /* |
| | | * Perform uninterrupted wait since this method is unlikely to block |
| | | * for a long time. |
| | |
| | | boolean wasInterrupted = false; |
| | | while (true) { |
| | | try { |
| | | cachedFutureLatch.await(); |
| | | cachedPromiseLatch.await(); |
| | | if (wasInterrupted) { |
| | | Thread.currentThread().interrupt(); |
| | | } |
| | | return cachedFuture; |
| | | return cachedPromise; |
| | | } catch (final InterruptedException e) { |
| | | wasInterrupted = true; |
| | | } |
| | |
| | | return true; |
| | | } |
| | | |
| | | void setFuture(final FutureResult<Result> future) { |
| | | cachedFuture = future; |
| | | cachedFutureLatch.countDown(); |
| | | void setPromise(final LdapPromise<Result> promise) { |
| | | cachedPromise = promise; |
| | | cachedPromiseLatch.countDown(); |
| | | } |
| | | |
| | | private void drainQueue() { |
| | |
| | | */ |
| | | return new AbstractAsynchronousConnection() { |
| | | @Override |
| | | public FutureResult<Void> abandonAsync(final AbandonRequest request) { |
| | | public LdapPromise<Void> abandonAsync(final AbandonRequest request) { |
| | | return connection.abandonAsync(request); |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> addAsync(final AddRequest request, |
| | | public LdapPromise<Result> addAsync(final AddRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | return connection.addAsync(withControls(request), intermediateResponseHandler); |
| | | } |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<BindResult> bindAsync(final BindRequest request, |
| | | public LdapPromise<BindResult> bindAsync(final BindRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | /* |
| | | * Simple brute force implementation in case the bind operation |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<CompareResult> compareAsync(final CompareRequest request, |
| | | public LdapPromise<CompareResult> compareAsync(final CompareRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | return connection.compareAsync(withControls(request), intermediateResponseHandler); |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> deleteAsync(final DeleteRequest request, |
| | | public LdapPromise<Result> deleteAsync(final DeleteRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | evict(request.getName()); |
| | | return connection.deleteAsync(withControls(request), intermediateResponseHandler); |
| | | } |
| | | |
| | | @Override |
| | | public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(final ExtendedRequest<R> request, |
| | | public <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(final ExtendedRequest<R> request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | /* |
| | | * Simple brute force implementation in case the extended |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> modifyAsync(final ModifyRequest request, |
| | | public LdapPromise<Result> modifyAsync(final ModifyRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | evict(request.getName()); |
| | | return connection.modifyAsync(withControls(request), intermediateResponseHandler); |
| | | } |
| | | |
| | | @Override |
| | | public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request, |
| | | public LdapPromise<Result> modifyDNAsync(final ModifyDNRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | // Simple brute force implementation: clear the cachedReads. |
| | | evictAll(); |
| | |
| | | * Try and re-use a cached result if possible. |
| | | */ |
| | | @Override |
| | | public FutureResult<Result> searchAsync(final SearchRequest request, |
| | | public LdapPromise<Result> searchAsync(final SearchRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler, final SearchResultHandler entryHandler) { |
| | | /* |
| | | * Don't attempt caching if this search is not a read (base |
| | |
| | | if (cachedRead != null && cachedRead.isMatchingRead(request)) { |
| | | // The cached read matches this read request. |
| | | cachedRead.addResultHandler(entryHandler); |
| | | return cachedRead.getFutureResult(); |
| | | return cachedRead.getPromise(); |
| | | } else { |
| | | // Cache the read, possibly evicting a non-matching cached read. |
| | | final CachedRead pendingCachedRead = new CachedRead(request, entryHandler); |
| | | synchronized (cachedReads) { |
| | | cachedReads.put(request.getName(), pendingCachedRead); |
| | | } |
| | | final FutureResult<Result> future = (FutureResult<Result>) connection |
| | | final LdapPromise<Result> promise = (LdapPromise<Result>) connection |
| | | .searchAsync(withControls(request), intermediateResponseHandler, pendingCachedRead) |
| | | .onSuccess(pendingCachedRead).onFailure(pendingCachedRead); |
| | | pendingCachedRead.setFuture(future); |
| | | return future; |
| | | pendingCachedRead.setPromise(promise); |
| | | return promise; |
| | | } |
| | | } |
| | | |
| | |
| | | * order and may cause the JSON resources to be returned in a different order to the |
| | | * order in which the primary LDAP search results were received. This is benign at |
| | | * the moment, but will need resolving when we implement server side sorting. A |
| | | * possible fix will be to use a queue of pending resources (futures?). However, |
| | | * possible fix will be to use a queue of pending resources (promises?). However, |
| | | * the queue cannot be unbounded in case it grows very large, but it cannot be |
| | | * bounded either since that could cause a deadlock between rest2ldap and the LDAP |
| | | * server (imagine the case where the server has a single worker thread which is |
| | |
| | | @Override |
| | | public void handleResult(Result result) { |
| | | if (subFilters.size() >= SEARCH_MAX_CANDIDATES) { |
| | | failureHandler.handleError(newErrorResult(ResultCode.ADMIN_LIMIT_EXCEEDED)); |
| | | failureHandler.handleError(newLdapException(ResultCode.ADMIN_LIMIT_EXCEEDED)); |
| | | } else if (subFilters.size() == 1) { |
| | | h.handleResult(subFilters.get(0)); |
| | | } else { |
| | |
| | | */ |
| | | package org.forgerock.opendj.server.core; |
| | | |
| | | import org.forgerock.opendj.ldap.FutureResult; |
| | | import org.forgerock.opendj.ldap.LdapPromise; |
| | | import org.forgerock.opendj.ldap.ResultHandler; |
| | | |
| | | /** |
| | |
| | | * The configuration to use when performing the backup. |
| | | * @param handler |
| | | * A handler which will be notified when the backup completes. |
| | | * @return A future representing the completion of the backup. |
| | | * @return A promise representing the completion of the backup. |
| | | */ |
| | | FutureResult<Void> createBackup(BackupConfig backupConfig, ResultHandler<Void> handler); |
| | | LdapPromise<Void> createBackup(BackupConfig backupConfig, ResultHandler<Void> handler); |
| | | |
| | | /** |
| | | * Returns the ID of this data provider. |
| | |
| | | * The configuration to use when performing the restore. |
| | | * @param handler |
| | | * A handler which will be notified when the restore completes. |
| | | * @return A future representing the completion of the restore. |
| | | * @return A promise representing the completion of the restore. |
| | | */ |
| | | FutureResult<Void> restoreBackup(RestoreConfig restoreConfig, ResultHandler<Void> handler); |
| | | LdapPromise<Void> restoreBackup(RestoreConfig restoreConfig, ResultHandler<Void> handler); |
| | | |
| | | /** |
| | | * Indicates whether this data provider provides a mechanism to perform a |
| | |
| | | */ |
| | | package org.forgerock.opendj.server.core; |
| | | |
| | | import org.forgerock.opendj.ldap.FutureResult; |
| | | import org.forgerock.opendj.ldap.LdapPromise; |
| | | import org.forgerock.opendj.ldap.ResultHandler; |
| | | import org.forgerock.opendj.ldif.EntryWriter; |
| | | |
| | |
| | | * The entry writer. |
| | | * @param handler |
| | | * A handler which will be notified when the export completes. |
| | | * @return A future representing the completion of the export. |
| | | * @return A promise representing the completion of the export. |
| | | */ |
| | | FutureResult<Void> exportEntries(EntryWriter writer, ResultHandler<Void> handler); |
| | | LdapPromise<Void> exportEntries(EntryWriter writer, ResultHandler<Void> handler); |
| | | |
| | | /** |
| | | * Returns the ID of this data provider. |
| | |
| | | */ |
| | | package org.forgerock.opendj.server.core; |
| | | |
| | | import org.forgerock.opendj.ldap.FutureResult; |
| | | import org.forgerock.opendj.ldap.LdapPromise; |
| | | import org.forgerock.opendj.ldap.ResultHandler; |
| | | import org.forgerock.opendj.ldif.EntryReader; |
| | | |
| | |
| | | * The entry reader. |
| | | * @param handler |
| | | * A handler which will be notified when the import completes. |
| | | * @return A future representing the completion of the import. |
| | | * @return A promise representing the completion of the import. |
| | | */ |
| | | FutureResult<Void> importEntries(EntryReader reader, ResultHandler<Void> handler); |
| | | LdapPromise<Void> importEntries(EntryReader reader, ResultHandler<Void> handler); |
| | | } |
| | |
| | | |
| | | import static org.forgerock.opendj.adapter.server2x.Converters.*; |
| | | import static org.forgerock.opendj.ldap.ByteString.*; |
| | | import static org.forgerock.opendj.ldap.LdapException.*; |
| | | import static org.forgerock.util.promise.Promises.*; |
| | | |
| | | /** |
| | |
| | | try { |
| | | return newConnection(new InternalClientConnection(to(dn))); |
| | | } catch (DirectoryException e) { |
| | | throw LdapException.newErrorResult(Responses.newResult(ResultCode.NO_SUCH_OBJECT)); |
| | | throw newLdapException(Responses.newResult(ResultCode.NO_SUCH_OBJECT)); |
| | | } |
| | | } |
| | | |
| | |
| | | bindClient.dispose(); |
| | | |
| | | } else { // not supported |
| | | throw LdapException.newErrorResult(Responses |
| | | .newResult(ResultCode.AUTH_METHOD_NOT_SUPPORTED)); |
| | | throw newLdapException(Responses.newResult(ResultCode.AUTH_METHOD_NOT_SUPPORTED)); |
| | | } |
| | | BindResult result = |
| | | Responses.newBindResult(getResultCode(bindOperation)); |
| | |
| | | if (result.isSuccess()) { |
| | | return result; |
| | | } else { |
| | | throw LdapException.newErrorResult(result); |
| | | throw newLdapException(result); |
| | | } |
| | | } |
| | | |
| | |
| | | import org.opends.server.types.LDAPException; |
| | | import org.opends.server.types.Operation; |
| | | |
| | | import static org.forgerock.opendj.ldap.LdapException.*; |
| | | |
| | | /** |
| | | * Common utility methods. |
| | | */ |
| | |
| | | if (result.isSuccess()) { |
| | | return result; |
| | | } else { |
| | | throw LdapException.newErrorResult(result); |
| | | throw newLdapException(result); |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | import static org.forgerock.opendj.adapter.server3x.Converters.*; |
| | | import static org.forgerock.opendj.ldap.ByteString.*; |
| | | import static org.forgerock.opendj.ldap.LdapException.*; |
| | | import static org.forgerock.util.promise.Promises.*; |
| | | |
| | | /** |
| | |
| | | try { |
| | | return newConnection(new InternalClientConnection(to(dn))); |
| | | } catch (DirectoryException e) { |
| | | throw LdapException.newErrorResult(Responses.newResult(ResultCode.NO_SUCH_OBJECT)); |
| | | throw newLdapException(Responses.newResult(ResultCode.NO_SUCH_OBJECT)); |
| | | } |
| | | } |
| | | |
| | |
| | | bindClient.dispose(); |
| | | |
| | | } else { // not supported |
| | | throw LdapException.newErrorResult(Responses |
| | | .newResult(ResultCode.AUTH_METHOD_NOT_SUPPORTED)); |
| | | throw newLdapException(Responses.newResult(ResultCode.AUTH_METHOD_NOT_SUPPORTED)); |
| | | } |
| | | BindResult result = Responses.newBindResult(bindOperation.getResultCode()); |
| | | result.setServerSASLCredentials(bindOperation.getSASLCredentials()); |
| | |
| | | if (result.isSuccess()) { |
| | | return result; |
| | | } else { |
| | | throw LdapException.newErrorResult(result); |
| | | throw newLdapException(result); |
| | | } |
| | | } |
| | | |
| | |
| | | import org.opends.server.types.LDAPException; |
| | | import org.opends.server.types.Operation; |
| | | |
| | | import static org.forgerock.opendj.ldap.LdapException.*; |
| | | |
| | | /** |
| | | * Common utility methods. |
| | | */ |
| | |
| | | if (result.isSuccess()) { |
| | | return result; |
| | | } else { |
| | | throw LdapException.newErrorResult(result); |
| | | throw newLdapException(result); |
| | | } |
| | | } |
| | | |