From 831467dfe7dbcda9a81bd6f082b073e31bd43897 Mon Sep 17 00:00:00 2001 From: Daniel Fisher Date: Thu, 12 Sep 2024 10:35:56 -0400 Subject: [PATCH] Add support for using SearchResultHandler with send. Add enum to define whether a SearchResultHandler is being used with the send or execute method. Update DefaultSearchOperationHandle to process handlers configured for send. --- .../handler/AbstractSearchResultHandler.java | 39 +++++++++++ .../ldaptive/handler/FreezeResultHandler.java | 30 +++++++-- .../ldaptive/handler/MergeResultHandler.java | 30 +++++++-- .../ldaptive/handler/SearchResultHandler.java | 25 +++++++- .../ldaptive/handler/SortResultHandler.java | 30 +++++++-- .../DefaultSearchOperationHandle.java | 64 +++++++++++++------ .../org/ldaptive/SearchOperationTest.java | 31 +++++++++ 7 files changed, 216 insertions(+), 33 deletions(-) create mode 100644 core/src/main/java/org/ldaptive/handler/AbstractSearchResultHandler.java diff --git a/core/src/main/java/org/ldaptive/handler/AbstractSearchResultHandler.java b/core/src/main/java/org/ldaptive/handler/AbstractSearchResultHandler.java new file mode 100644 index 000000000..a27cf7c5c --- /dev/null +++ b/core/src/main/java/org/ldaptive/handler/AbstractSearchResultHandler.java @@ -0,0 +1,39 @@ +/* See LICENSE for licensing and NOTICE for copyright. */ +package org.ldaptive.handler; + +/** + * Base class for search result handlers. + * + * @author Middleware Services + */ +public abstract class AbstractSearchResultHandler implements SearchResultHandler +{ + + /** Handler usage. */ + private final Usage usage; + + + /** + * Creates a new abstract search result handler. + * + * @param u handler usage + */ + public AbstractSearchResultHandler(final Usage u) + { + usage = u; + } + + + @Override + public Usage getUsage() + { + return usage; + } + + + @Override + public String toString() + { + return getClass().getName() + "@" + hashCode() + "::" + "usage=" + usage; + } +} diff --git a/core/src/main/java/org/ldaptive/handler/FreezeResultHandler.java b/core/src/main/java/org/ldaptive/handler/FreezeResultHandler.java index 36bcd310f..8e324d566 100644 --- a/core/src/main/java/org/ldaptive/handler/FreezeResultHandler.java +++ b/core/src/main/java/org/ldaptive/handler/FreezeResultHandler.java @@ -9,13 +9,31 @@ * * @author Middleware Services */ -public class FreezeResultHandler implements SearchResultHandler +public class FreezeResultHandler extends AbstractSearchResultHandler { /** hash code seed. */ private static final int HASH_CODE_SEED = 859; + /** Default constructor. */ + public FreezeResultHandler() + { + this(Usage.SYNC); + } + + + /** + * Creates a new freeze result handler. + * + * @param u handler usage + */ + public FreezeResultHandler(final Usage u) + { + super(u); + } + + @Override public SearchResponse apply(final SearchResponse response) { @@ -30,20 +48,24 @@ public boolean equals(final Object o) if (o == this) { return true; } - return o instanceof FreezeResultHandler; + if (o instanceof FreezeResultHandler) { + final FreezeResultHandler v = (FreezeResultHandler) o; + return LdapUtils.areEqual(getUsage(), v.getUsage()); + } + return false; } @Override public int hashCode() { - return LdapUtils.computeHashCode(HASH_CODE_SEED); + return LdapUtils.computeHashCode(HASH_CODE_SEED, getUsage()); } @Override public String toString() { - return "[" + getClass().getName() + "@" + hashCode() + "]"; + return "[" + super.toString() + "]"; } } diff --git a/core/src/main/java/org/ldaptive/handler/MergeResultHandler.java b/core/src/main/java/org/ldaptive/handler/MergeResultHandler.java index 72724844f..d101ec15c 100644 --- a/core/src/main/java/org/ldaptive/handler/MergeResultHandler.java +++ b/core/src/main/java/org/ldaptive/handler/MergeResultHandler.java @@ -12,13 +12,31 @@ * * @author Miguel Martinez de Espronceda */ -public class MergeResultHandler implements SearchResultHandler +public class MergeResultHandler extends AbstractSearchResultHandler { /** hash code seed. */ private static final int HASH_CODE_SEED = 857; + /** Default constructor. */ + public MergeResultHandler() + { + this(Usage.SYNC); + } + + + /** + * Creates a new merge result handler. + * + * @param u handler usage + */ + public MergeResultHandler(final Usage u) + { + super(u); + } + + @Override public SearchResponse apply(final SearchResponse searchResponse) { @@ -82,20 +100,24 @@ public boolean equals(final Object o) if (o == this) { return true; } - return o instanceof MergeResultHandler; + if (o instanceof MergeResultHandler) { + final MergeResultHandler v = (MergeResultHandler) o; + return LdapUtils.areEqual(getUsage(), v.getUsage()); + } + return false; } @Override public int hashCode() { - return LdapUtils.computeHashCode(HASH_CODE_SEED); + return LdapUtils.computeHashCode(HASH_CODE_SEED, getUsage()); } @Override public String toString() { - return "[" + getClass().getName() + "@" + hashCode() + "]"; + return "[" + super.toString() + "]"; } } diff --git a/core/src/main/java/org/ldaptive/handler/SearchResultHandler.java b/core/src/main/java/org/ldaptive/handler/SearchResultHandler.java index 2a7fa048e..22e3c09f2 100644 --- a/core/src/main/java/org/ldaptive/handler/SearchResultHandler.java +++ b/core/src/main/java/org/ldaptive/handler/SearchResultHandler.java @@ -9,4 +9,27 @@ * * @author Middleware Services */ -public interface SearchResultHandler extends Function {} +public interface SearchResultHandler extends Function +{ + + + /** Intended usage of the handler. */ + enum Usage { + /** Use this handler with {@link org.ldaptive.SearchOperation#send()}*/ + ASYNC, + + /** Use this handler with {@link org.ldaptive.SearchOperation#execute()}*/ + SYNC + } + + + /** + * Returns the usage for this handler. + * + * @return handler usage + */ + default Usage getUsage() + { + return Usage.SYNC; + } +} diff --git a/core/src/main/java/org/ldaptive/handler/SortResultHandler.java b/core/src/main/java/org/ldaptive/handler/SortResultHandler.java index dacb230f6..55f928aae 100644 --- a/core/src/main/java/org/ldaptive/handler/SortResultHandler.java +++ b/core/src/main/java/org/ldaptive/handler/SortResultHandler.java @@ -9,13 +9,31 @@ * * @author Middleware Services */ -public class SortResultHandler implements SearchResultHandler +public class SortResultHandler extends AbstractSearchResultHandler { /** hash code seed. */ private static final int HASH_CODE_SEED = 853; + /** Default constructor. */ + public SortResultHandler() + { + this(Usage.SYNC); + } + + + /** + * Creates a new sort result handler. + * + * @param u handler usage + */ + public SortResultHandler(final Usage u) + { + super(u); + } + + @Override public SearchResponse apply(final SearchResponse response) { @@ -29,20 +47,24 @@ public boolean equals(final Object o) if (o == this) { return true; } - return o instanceof SortResultHandler; + if (o instanceof SortResultHandler) { + final SortResultHandler v = (SortResultHandler) o; + return LdapUtils.areEqual(getUsage(), v.getUsage()); + } + return false; } @Override public int hashCode() { - return LdapUtils.computeHashCode(HASH_CODE_SEED); + return LdapUtils.computeHashCode(HASH_CODE_SEED, getUsage()); } @Override public String toString() { - return "[" + getClass().getName() + "@" + hashCode() + "]"; + return "[" + super.toString() + "]"; } } diff --git a/core/src/main/java/org/ldaptive/transport/DefaultSearchOperationHandle.java b/core/src/main/java/org/ldaptive/transport/DefaultSearchOperationHandle.java index 97c71c294..b94884431 100644 --- a/core/src/main/java/org/ldaptive/transport/DefaultSearchOperationHandle.java +++ b/core/src/main/java/org/ldaptive/transport/DefaultSearchOperationHandle.java @@ -108,25 +108,27 @@ public SearchResponse await() SearchResponse done = super.await(); if (onSearchResult != null) { for (SearchResultHandler func : onSearchResult) { - final SearchResponse handlerResponse; - try { - handlerResponse = func.apply(done); - } catch (Exception ex) { - if (ex.getCause() instanceof LdapException) { - throw (LdapException) ex.getCause(); + if (func.getUsage() == SearchResultHandler.Usage.SYNC) { + final SearchResponse handlerResponse; + try { + handlerResponse = func.apply(done); + } catch (Exception ex) { + if (ex.getCause() instanceof LdapException) { + throw (LdapException) ex.getCause(); + } + throw new IllegalStateException("Search result handler " + func + " threw exception", ex); } - throw new IllegalStateException("Search result handler " + func + " threw exception", ex); - } - if (handlerResponse == null) { - throw new IllegalStateException("Search result handler " + func + " returned null result"); - } else if (!handlerResponse.equalsResult(done) && - // FollowSearchReferralHandler can be used as a referral handler and a search result handler for convenience - // allow it to modify the search response - !(ResultCode.REFERRAL == done.getResultCode() && FollowSearchReferralHandler.class.equals(func.getClass()))) - { - throw new IllegalStateException("Cannot modify search result instance with handler " + func); + if (handlerResponse == null) { + throw new IllegalStateException("Search result handler " + func + " returned null result"); + } else if (!handlerResponse.equalsResult(done) && + // FollowSearchReferralHandler can be used as a referral handler and a search result handler for convenience + // allow it to modify the search response + !(ResultCode.REFERRAL == done.getResultCode() && FollowSearchReferralHandler.class.equals(func.getClass()))) + { + throw new IllegalStateException("Cannot modify search result instance with handler " + func); + } + done = handlerResponse; } - done = handlerResponse; } } super.evaluateThrowCondition(done); @@ -324,11 +326,33 @@ public void result(final SearchResponse r) processResult(r); r.addEntries(result.getEntries()); r.addReferences(result.getReferences()); + SearchResponse done = r; if (SORT_RESULTS) { - finalizeResult(SearchResponse.sort(r)); - } else { - finalizeResult(r); + done = SearchResponse.sort(r); + } + if (onSearchResult != null) { + for (SearchResultHandler func : onSearchResult) { + if (func.getUsage() == SearchResultHandler.Usage.ASYNC) { + final SearchResponse handlerResponse; + try { + handlerResponse = func.apply(done); + if (handlerResponse == null) { + throw new IllegalStateException("Search result handler " + func + " returned null result"); + } else if (!handlerResponse.equalsResult(done)) { + throw new IllegalStateException("Cannot modify search result instance with handler " + func); + } + done = handlerResponse; + } catch (Exception ex) { + if (ex.getCause() instanceof LdapException) { + exception((LdapException) ex.getCause()); + } else { + logger.warn("Search result function {} in handle {} threw an exception", func, this, ex); + } + } + } + } } + finalizeResult(done); } diff --git a/integration/src/test/java/org/ldaptive/SearchOperationTest.java b/integration/src/test/java/org/ldaptive/SearchOperationTest.java index c3b7ca29c..80118afee 100644 --- a/integration/src/test/java/org/ldaptive/SearchOperationTest.java +++ b/integration/src/test/java/org/ldaptive/SearchOperationTest.java @@ -9,6 +9,9 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import org.ldaptive.ad.control.ForceUpdateControl; import org.ldaptive.ad.control.GetStatsControl; @@ -42,6 +45,7 @@ import org.ldaptive.handler.NoOpEntryHandler; import org.ldaptive.handler.RecursiveResultHandler; import org.ldaptive.handler.ResultPredicate; +import org.ldaptive.handler.SearchResultHandler; import org.ldaptive.referral.DefaultReferralConnectionFactory; import org.ldaptive.referral.FollowSearchReferralHandler; import org.ldaptive.referral.FollowSearchResultReferenceHandler; @@ -1091,6 +1095,33 @@ public void mergeDuplicateSearch( final SearchResponse result = search.execute(sr); // ignore the case of member and contactPerson; some directories return those in mixed case LdapEntryAssert.assertThat(result.getEntry()).isSame(convertLdifToEntry(expected), "member", "contactPerson"); + + // perform search again using send operation + final CountDownLatch latch = new CountDownLatch(1); + final AtomicReference ref = new AtomicReference<>(); + search.setSearchResultHandlers( + new MergeResultHandler(SearchResultHandler.Usage.ASYNC), + new SearchResultHandler() { + @Override + public SearchResponse apply(final SearchResponse sr) + { + ref.set(sr); + latch.countDown(); + return sr; + } + @Override + public Usage getUsage() + { + return Usage.ASYNC; + } + }); + + search.send(sr); + if (!latch.await(Duration.ofSeconds(5).toMillis(), TimeUnit.MILLISECONDS)) { + fail("No results received"); + } + // ignore the case of member and contactPerson; some directories return those in mixed case + LdapEntryAssert.assertThat(ref.get().getEntry()).isSame(convertLdifToEntry(expected), "member", "contactPerson"); }