All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ldaptive.provider.jldap.JLdapConnection Maven / Gradle / Ivy

There is a newer version: 1.3.3
Show newest version
/* See LICENSE for licensing and NOTICE for copyright. */
package org.ldaptive.provider.jldap;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import com.novell.ldap.LDAPConnection;
import com.novell.ldap.LDAPConstraints;
import com.novell.ldap.LDAPControl;
import com.novell.ldap.LDAPEntry;
import com.novell.ldap.LDAPException;
import com.novell.ldap.LDAPExtendedOperation;
import com.novell.ldap.LDAPExtendedResponse;
import com.novell.ldap.LDAPIntermediateResponse;
import com.novell.ldap.LDAPMessage;
import com.novell.ldap.LDAPMessageQueue;
import com.novell.ldap.LDAPResponse;
import com.novell.ldap.LDAPResponseQueue;
import com.novell.ldap.LDAPSearchConstraints;
import com.novell.ldap.LDAPSearchQueue;
import com.novell.ldap.LDAPSearchResult;
import com.novell.ldap.LDAPSearchResultReference;
import com.novell.ldap.LDAPUnsolicitedNotificationListener;
import com.novell.security.sasl.RealmCallback;
import com.novell.security.sasl.RealmChoiceCallback;
import org.ldaptive.AddRequest;
import org.ldaptive.BindRequest;
import org.ldaptive.CompareRequest;
import org.ldaptive.DeleteRequest;
import org.ldaptive.DerefAliases;
import org.ldaptive.LdapException;
import org.ldaptive.ModifyDnRequest;
import org.ldaptive.ModifyRequest;
import org.ldaptive.Request;
import org.ldaptive.Response;
import org.ldaptive.ResultCode;
import org.ldaptive.SearchEntry;
import org.ldaptive.SearchReference;
import org.ldaptive.SearchRequest;
import org.ldaptive.SearchScope;
import org.ldaptive.async.AsyncRequest;
import org.ldaptive.control.RequestControl;
import org.ldaptive.control.ResponseControl;
import org.ldaptive.extended.ExtendedRequest;
import org.ldaptive.extended.ExtendedResponse;
import org.ldaptive.extended.ExtendedResponseFactory;
import org.ldaptive.extended.UnsolicitedNotificationListener;
import org.ldaptive.intermediate.IntermediateResponseFactory;
import org.ldaptive.provider.ProviderConnection;
import org.ldaptive.provider.ProviderUtils;
import org.ldaptive.provider.SearchItem;
import org.ldaptive.provider.SearchIterator;
import org.ldaptive.provider.SearchListener;
import org.ldaptive.sasl.SaslConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * JLDAP provider implementation of ldap operations.
 *
 * @author  Middleware Services
 */
public class JLdapConnection implements ProviderConnection
{

  /** Logger for this class. */
  protected final Logger logger = LoggerFactory.getLogger(getClass());

  /** Ldap connection. */
  private LDAPConnection connection;

  /** Provider configuration. */
  private final JLdapProviderConfig config;

  /** Receives unsolicited notifications. */
  private final AggregateUnsolicitedNotificationListener notificationListener =
    new AggregateUnsolicitedNotificationListener();


  /**
   * Creates a new jldap connection.
   *
   * @param  conn  ldap connection
   * @param  pc  provider configuration
   */
  public JLdapConnection(final LDAPConnection conn, final JLdapProviderConfig pc)
  {
    connection = conn;
    config = pc;
    connection.addUnsolicitedNotificationListener(notificationListener);
  }


  /**
   * Returns the underlying ldap connection.
   *
   * @return  ldap connection
   */
  public LDAPConnection getLDAPConnection()
  {
    return connection;
  }


  @Override
  public void close(final RequestControl[] controls)
    throws LdapException
  {
    try {
      if (connection != null) {
        connection.disconnect(getLDAPConstraints(controls));
      }
    } catch (LDAPException e) {
      throw new LdapException(e, ResultCode.valueOf(e.getResultCode()));
    } finally {
      connection = null;
    }
  }


  @Override
  public Response bind(final BindRequest request)
    throws LdapException
  {
    final Response response;
    if (request.getSaslConfig() != null) {
      response = saslBind(request);
    } else if (request.getDn() == null && request.getCredential() == null) {
      response = anonymousBind(request);
    } else {
      response = simpleBind(request);
    }
    return response;
  }


  /**
   * Performs an anonymous bind.
   *
   * @param  request  to bind with
   *
   * @return  bind response
   *
   * @throws  LdapException  if an error occurs
   */
  protected Response anonymousBind(final BindRequest request)
    throws LdapException
  {
    Response response = null;
    try {
      final LDAPResponseQueue queue = connection.bind(
        LDAPConnection.LDAP_V3,
        null,
        null,
        null,
        getLDAPConstraints(request));
      final LDAPResponse lr = (LDAPResponse) queue.getResponse();
      response = createResponse(request, null, lr);
    } catch (LDAPException e) {
      processLDAPException(e);
    }
    return response;
  }


  /**
   * Performs a simple bind.
   *
   * @param  request  to bind with
   *
   * @return  bind response
   *
   * @throws  LdapException  if an error occurs
   */
  protected Response simpleBind(final BindRequest request)
    throws LdapException
  {
    Response response = null;
    try {
      final LDAPResponseQueue queue = connection.bind(
        LDAPConnection.LDAP_V3,
        request.getDn(),
        request.getCredential().getBytes(),
        null,
        getLDAPConstraints(request));
      final LDAPResponse lr = (LDAPResponse) queue.getResponse();
      response = createResponse(request, null, lr);
    } catch (LDAPException e) {
      processLDAPException(e);
    }
    return response;
  }


  /**
   * Performs a sasl bind.
   *
   * @param  request  to bind with
   *
   * @return  bind response
   *
   * @throws  LdapException  if an error occurs
   */
  protected Response saslBind(final BindRequest request)
    throws LdapException
  {
    try {
      final SaslConfig sc = request.getSaslConfig();
      switch (sc.getMechanism()) {

      case EXTERNAL:
        throw new UnsupportedOperationException("SASL External not supported");
        /* current implementation appears to be broken
         * see http://tinyurl.com/7ojdzlz
         * connection.bind(
         * (String) null,
         * sc.getAuthorizationId(),
         * new String[] {"EXTERNAL"},
         * null,
         * (Object) null);
         * break;
         */

      case DIGEST_MD5:
        connection.bind(
          null,
          request.getDn(),
          new String[] {"DIGEST-MD5"},
          null,
          new SaslCallbackHandler(null, request.getCredential() != null ? request.getCredential().getString() : null));
        break;

      case CRAM_MD5:
        throw new UnsupportedOperationException("CRAM-MD5 not supported");

      case GSSAPI:
        throw new UnsupportedOperationException("GSSAPI not supported");

      default:
        throw new IllegalArgumentException("Unknown SASL authentication mechanism: " + sc.getMechanism());
      }
    } catch (LDAPException e) {
      processLDAPException(e);
    }
    return new Response<>(null, ResultCode.SUCCESS);
  }


  @Override
  public Response add(final AddRequest request)
    throws LdapException
  {
    Response response = null;
    try {
      final JLdapUtils bu = new JLdapUtils();
      final LDAPResponseQueue queue = connection.add(
        new LDAPEntry(request.getDn(), bu.fromLdapAttributes(request.getLdapAttributes())),
        null,
        getLDAPConstraints(request));
      final LDAPResponse lr = (LDAPResponse) queue.getResponse();
      response = createResponse(request, null, lr);
    } catch (LDAPException e) {
      processLDAPException(e);
    }
    return response;
  }


  @Override
  public Response compare(final CompareRequest request)
    throws LdapException
  {
    Response response = null;
    try {
      final JLdapUtils bu = new JLdapUtils();
      final LDAPResponseQueue queue = connection.compare(
        request.getDn(),
        bu.fromLdapAttribute(request.getAttribute()),
        null,
        getLDAPConstraints(request));
      final LDAPResponse lr = (LDAPResponse) queue.getResponse();
      response = createResponse(request, lr.getResultCode() == ResultCode.COMPARE_TRUE.value(), lr);
    } catch (LDAPException e) {
      processLDAPException(e);
    }
    return response;
  }


  @Override
  public Response delete(final DeleteRequest request)
    throws LdapException
  {
    Response response = null;
    try {
      final LDAPResponseQueue queue = connection.delete(request.getDn(), null, getLDAPConstraints(request));
      final LDAPResponse lr = (LDAPResponse) queue.getResponse();
      response = createResponse(request, null, lr);
    } catch (LDAPException e) {
      processLDAPException(e);
    }
    return response;
  }


  @Override
  public Response modify(final ModifyRequest request)
    throws LdapException
  {
    Response response = null;
    try {
      final JLdapUtils bu = new JLdapUtils();
      final LDAPResponseQueue queue = connection.modify(
        request.getDn(),
        bu.fromAttributeModification(request.getAttributeModifications()),
        null,
        getLDAPConstraints(request));
      final LDAPResponse lr = (LDAPResponse) queue.getResponse();
      response = createResponse(request, null, lr);
    } catch (LDAPException e) {
      processLDAPException(e);
    }
    return response;
  }


  @Override
  public Response modifyDn(final ModifyDnRequest request)
    throws LdapException
  {
    Response response = null;
    try {
      final String[] dn = request.getNewDn().split(",", 2);
      final LDAPResponseQueue queue = connection.rename(
        request.getDn(),
        dn[0],
        dn[1],
        request.getDeleteOldRDn(),
        null,
        getLDAPConstraints(request));
      final LDAPResponse lr = (LDAPResponse) queue.getResponse();
      response = createResponse(request, null, lr);
    } catch (LDAPException e) {
      processLDAPException(e);
    }
    return response;
  }


  @Override
  public SearchIterator search(final SearchRequest request)
    throws LdapException
  {
    final JLdapSearchIterator i = new JLdapSearchIterator(request);
    i.initialize();
    return i;
  }


  @Override
  public void searchAsync(final SearchRequest request, final SearchListener listener)
    throws LdapException
  {
    final JLdapAsyncSearchListener l = new JLdapAsyncSearchListener(request, listener);
    l.initialize();
  }


  @Override
  public void abandon(final int messageId, final RequestControl[] controls)
    throws LdapException
  {
    try {
      connection.abandon(messageId, getLDAPConstraints(controls));
    } catch (LDAPException e) {
      processLDAPException(e);
    }
  }


  @Override
  public Response extendedOperation(final ExtendedRequest request)
    throws LdapException
  {
    Response response = null;
    try {
      final LDAPExtendedResponse ldapExtRes = connection.extendedOperation(
        new LDAPExtendedOperation(request.getOID(), request.encode()),
        getLDAPConstraints(request));
      final ExtendedResponse extRes = ExtendedResponseFactory.createExtendedResponse(
        request.getOID(),
        ldapExtRes.getID(),
        ldapExtRes.getValue());
      response = createResponse(request, extRes.getValue(), ldapExtRes);
    } catch (LDAPException e) {
      processLDAPException(e);
    }
    return response;
  }


  @Override
  public void addUnsolicitedNotificationListener(final UnsolicitedNotificationListener listener)
  {
    notificationListener.addUnsolicitedNotificationListener(listener);
  }


  @Override
  public void removeUnsolicitedNotificationListener(final UnsolicitedNotificationListener listener)
  {
    notificationListener.removeUnsolicitedNotificationListener(listener);
  }


  /**
   * Returns an ldap constraints object configured with the supplied request.
   *
   * @param  request  request containing configuration to create constraints
   *
   * @return  ldap constraints
   */
  protected LDAPConstraints getLDAPConstraints(final Request request)
  {
    LDAPConstraints constraints = connection.getConstraints();
    if (constraints == null) {
      constraints = new LDAPConstraints();
    }
    if (request.getControls() != null) {
      constraints.setControls(config.getControlProcessor().processRequestControls(request.getControls()));
    }
    return constraints;
  }


  /**
   * Returns an ldap constraints object configured with the supplied controls.
   *
   * @param  controls  to sets in the constraints
   *
   * @return  ldap constraints
   */
  protected LDAPConstraints getLDAPConstraints(final RequestControl[] controls)
  {
    LDAPConstraints constraints = connection.getConstraints();
    if (constraints == null) {
      constraints = new LDAPConstraints();
    }
    if (controls != null) {
      constraints.setControls(config.getControlProcessor().processRequestControls(controls));
    }
    return constraints;
  }


  /**
   * Determines if the supplied response should result in an operation retry.
   *
   * @param  request  that produced the exception
   * @param  ldapResponse  provider response
   *
   * @throws  LdapException  wrapping the ldap exception
   */
  protected void throwOperationException(final Request request, final LDAPResponse ldapResponse)
    throws LdapException
  {
    ProviderUtils.throwOperationException(
      config.getOperationExceptionResultCodes(),
      String.format("Ldap returned result code: %s", ldapResponse.getResultCode()),
      ldapResponse.getResultCode(),
      ldapResponse.getMatchedDN(),
      config.getControlProcessor().processResponseControls(ldapResponse.getControls()),
      ldapResponse.getReferrals(),
      false);
  }


  /**
   * Creates an operation response with the supplied response data.
   *
   * @param    type of response
   * @param  request  containing controls
   * @param  result  of the operation
   * @param  ldapResponse  provider response
   *
   * @return  operation response
   */
  protected  Response createResponse(final Request request, final T result, final LDAPResponse ldapResponse)
  {
    return
      new Response<>(
        result,
        ResultCode.valueOf(ldapResponse.getResultCode()),
        ldapResponse.getErrorMessage(),
        ldapResponse.getMatchedDN(),
        config.getControlProcessor().processResponseControls(ldapResponse.getControls()),
        ldapResponse.getReferrals(),
        ldapResponse.getMessageID());
  }


  /**
   * Determines if the supplied ldap exception should result in an operation retry.
   *
   * @param  e  that was produced
   *
   * @throws  LdapException  wrapping the ldap exception
   */
  protected void processLDAPException(final LDAPException e)
    throws LdapException
  {
    ProviderUtils.throwOperationException(
      config.getOperationExceptionResultCodes(),
      e,
      e.getResultCode(),
      e.getMatchedDN(),
      null,
      null,
      true);
  }


  /** Callback handler used by SASL mechanisms. */
  private static class SaslCallbackHandler implements CallbackHandler
  {

    /** user name. */
    private final String user;

    /** password. */
    private final char[] pass;


    /**
     * Creates a new bind callback handler.
     *
     * @param  u  username to bind with
     * @param  p  password to bind with
     */
    SaslCallbackHandler(final String u, final String p)
    {
      user = u;
      if (p != null) {
        pass = p.toCharArray();
      } else {
        pass = null;
      }
    }


    @Override
    public void handle(final Callback[] callbacks)
      throws IOException, UnsupportedCallbackException
    {
      for (Callback cb : callbacks) {
        if (cb instanceof NameCallback) {
          // if user is null, the authzId will be used as it's the default name
          ((NameCallback) cb).setName(user != null ? user : ((NameCallback) cb).getDefaultName());
        } else if (cb instanceof PasswordCallback) {
          ((PasswordCallback) cb).setPassword(pass);
        } else if (cb instanceof RealmCallback) {
          ((RealmCallback) cb).setText(((RealmCallback) cb).getDefaultText());
        } else if (cb instanceof RealmChoiceCallback) {
          ((RealmChoiceCallback) cb).setSelectedIndex(0);
        }
      }
    }
  }


  /** Search iterator for JLdap search results. */
  protected class JLdapSearchIterator extends AbstractJLdapSearch implements SearchIterator
  {

    /** Response data. */
    private Response response;

    /** Ldap search result iterator. */
    private SearchResultIterator resultIterator;


    /**
     * Creates a new jldap search iterator.
     *
     * @param  sr  search request
     */
    public JLdapSearchIterator(final SearchRequest sr)
    {
      super(sr);
    }


    /**
     * Initializes this jldap search iterator.
     *
     * @throws  LdapException  if an error occurs
     */
    public void initialize()
      throws LdapException
    {
      try {
        resultIterator = new SearchResultIterator(search(connection, request));
      } catch (LDAPException e) {
        processLDAPException(e);
      }
    }


    @Override
    public boolean hasNext()
      throws LdapException
    {
      if (resultIterator == null || response != null) {
        return false;
      }

      boolean more = false;
      try {
        more = resultIterator.hasNext();
        if (!more) {
          final LDAPResponse res = resultIterator.getResponse();
          logger.trace("reading search response: {}", res);
          throwOperationException(request, res);
          response = createResponse(request, null, res);
        }
      } catch (LDAPException e) {
        final ResultCode rc = ignoreSearchException(config.getSearchIgnoreResultCodes(), e);
        if (rc == null) {
          processLDAPException(e);
        }
        response = new Response<>(null, rc, e.getLDAPErrorMessage(), null, null, null, -1);
      }
      return more;
    }


    @Override
    public SearchItem next()
      throws LdapException
    {
      final SearchItem si;
      final LDAPMessage message = resultIterator.next();
      if (message instanceof LDAPSearchResult) {
        si = processLDAPSearchResult((LDAPSearchResult) message);
      } else if (message instanceof LDAPSearchResultReference) {
        si = processLDAPSearchResultReference((LDAPSearchResultReference) message);
      } else if (message instanceof LDAPIntermediateResponse) {
        si = processLDAPIntermediateResponse((LDAPIntermediateResponse) message);
      } else {
        throw new IllegalStateException("Unknown message: " + message);
      }
      return si;
    }


    @Override
    public Response getResponse()
    {
      return response;
    }


    @Override
    public void close()
      throws LdapException {}
  }


  /** Async search listener for JLdap search results. */
  protected class JLdapAsyncSearchListener extends AbstractJLdapSearch
  {

    /** Search result listener. */
    private final SearchListener listener;


    /**
     * Creates a new jldap async search listener.
     *
     * @param  sr  search request
     * @param  sl  search listener
     */
    public JLdapAsyncSearchListener(final SearchRequest sr, final SearchListener sl)
    {
      super(sr);
      listener = sl;
    }


    /**
     * Initializes this jldap search listener.
     *
     * @throws  LdapException  if an error occurs
     */
    public void initialize()
      throws LdapException
    {
      try {
        search(connection, request);
      } catch (LDAPException e) {
        processLDAPException(e);
      }
    }


    /**
     * Executes an ldap search.
     *
     * @param  conn  to search with
     * @param  sr  to read properties from
     *
     * @return  ldap search queue
     *
     * @throws  LDAPException  if an error occurs
     */
    protected LDAPSearchQueue search(final LDAPConnection conn, final SearchRequest sr)
      throws LDAPException
    {
      final SearchResultIterator i = new SearchResultIterator(super.search(conn, sr));
      listener.asyncRequestReceived(new JLdapAsyncRequest(i.getLDAPSearchQueue()));
      while (i.hasNext()) {
        final LDAPMessage message = i.next();
        if (message instanceof LDAPSearchResult) {
          listener.searchItemReceived(processLDAPSearchResult((LDAPSearchResult) message));
        } else if (message instanceof LDAPSearchResultReference) {
          listener.searchItemReceived(processLDAPSearchResultReference((LDAPSearchResultReference) message));
        } else if (message instanceof LDAPIntermediateResponse) {
          listener.searchItemReceived(processLDAPIntermediateResponse((LDAPIntermediateResponse) message));
        } else {
          throw new IllegalStateException("Unknown message: " + message);
        }
      }

      final Response response = createResponse(request, null, i.getResponse());
      listener.responseReceived(response);
      return null;
    }
  }


  /** Common search functionality for jldap iterators and listeners. */
  protected abstract class AbstractJLdapSearch
  {

    /** Search request. */
    protected final SearchRequest request;

    /** Utility class. */
    protected final JLdapUtils util;


    /**
     * Creates a new abstract jldap search.
     *
     * @param  sr  search request
     */
    public AbstractJLdapSearch(final SearchRequest sr)
    {
      request = sr;
      util = new JLdapUtils(request.getSortBehavior());
      util.setBinaryAttributes(request.getBinaryAttributes());
    }


    /**
     * Executes an ldap search.
     *
     * @param  conn  to search with
     * @param  sr  to read properties from
     *
     * @return  ldap search queue
     *
     * @throws  LDAPException  if an error occurs
     */
    protected LDAPSearchQueue search(final LDAPConnection conn, final SearchRequest sr)
      throws LDAPException
    {
      final LDAPSearchConstraints constraints = getLDAPSearchConstraints(sr);
      final LDAPControl[] lc = config.getControlProcessor().processRequestControls(sr.getControls());
      if (lc != null) {
        constraints.setControls(lc);
      }
      return
        conn.search(
          sr.getBaseDn(),
          getSearchScope(sr.getSearchScope()),
          sr.getSearchFilter() != null ? sr.getSearchFilter().format() : null,
          sr.getReturnAttributes(),
          sr.getTypesOnly(),
          null,
          constraints);
    }


    /**
     * Returns the jldap integer constant for the supplied search scope.
     *
     * @param  ss  search scope
     *
     * @return  integer constant
     */
    protected int getSearchScope(final SearchScope ss)
    {
      int scope = -1;
      if (ss == SearchScope.OBJECT) {
        scope = LDAPConnection.SCOPE_BASE;
      } else if (ss == SearchScope.ONELEVEL) {
        scope = LDAPConnection.SCOPE_ONE;
      } else if (ss == SearchScope.SUBTREE) {
        scope = LDAPConnection.SCOPE_SUB;
      }
      return scope;
    }


    /**
     * Returns an ldap search constraints object configured with the supplied search request.
     *
     * @param  sr  search request containing configuration to create search constraints
     *
     * @return  ldap search constraints
     */
    protected LDAPSearchConstraints getLDAPSearchConstraints(final SearchRequest sr)
    {
      LDAPSearchConstraints constraints = connection.getSearchConstraints();
      if (constraints == null) {
        constraints = new LDAPSearchConstraints();
      }
      constraints.setServerTimeLimit((int) sr.getTimeLimit().getSeconds());
      constraints.setMaxResults(Long.valueOf(sr.getSizeLimit()).intValue());
      if (sr.getDerefAliases() != null) {
        if (sr.getDerefAliases() == DerefAliases.ALWAYS) {
          constraints.setDereference(LDAPSearchConstraints.DEREF_ALWAYS);
        } else if (sr.getDerefAliases() == DerefAliases.FINDING) {
          constraints.setDereference(LDAPSearchConstraints.DEREF_FINDING);
        } else if (sr.getDerefAliases() == DerefAliases.NEVER) {
          constraints.setDereference(LDAPSearchConstraints.DEREF_NEVER);
        } else if (sr.getDerefAliases() == DerefAliases.SEARCHING) {
          constraints.setDereference(LDAPSearchConstraints.DEREF_SEARCHING);
        }
      }
      return constraints;
    }


    /**
     * Determines whether the supplied ldap exception should be ignored.
     *
     * @param  ignoreResultCodes  to match against the exception
     * @param  e  ldap exception to match
     *
     * @return  result code that should be ignored or null
     */
    protected ResultCode ignoreSearchException(final ResultCode[] ignoreResultCodes, final LDAPException e)
    {
      ResultCode ignore = null;
      if (ignoreResultCodes != null && ignoreResultCodes.length > 0) {
        for (ResultCode rc : ignoreResultCodes) {
          if (e.getResultCode() == rc.value()) {
            logger.debug("Ignoring ldap exception", e);
            ignore = rc;
            break;
          }
        }
      }
      return ignore;
    }


    /**
     * Processes the response controls on the supplied result and returns a corresponding search item.
     *
     * @param  res  to process
     *
     * @return  search item
     */
    protected SearchItem processLDAPSearchResult(final LDAPSearchResult res)
    {
      logger.trace("reading search result: {}", res);

      ResponseControl[] respControls = null;
      if (res.getControls() != null && res.getControls().length > 0) {
        respControls = config.getControlProcessor().processResponseControls(res.getControls());
      }

      final SearchEntry se = util.toSearchEntry(res.getEntry(), respControls, res.getMessageID());
      return new SearchItem(se);
    }


    /**
     * Processes the response controls on the supplied reference and returns a corresponding search item.
     *
     * @param  ref  to process
     *
     * @return  search item
     */
    protected SearchItem processLDAPSearchResultReference(final LDAPSearchResultReference ref)
    {
      logger.trace("reading search reference: {}", ref);

      ResponseControl[] respControls = null;
      if (ref.getControls() != null && ref.getControls().length > 0) {
        respControls = config.getControlProcessor().processResponseControls(ref.getControls());
      }

      final SearchReference sr = new SearchReference(ref.getMessageID(), respControls, ref.getReferrals());
      return new SearchItem(sr);
    }


    /**
     * Processes the response controls on the supplied response and returns a corresponding search item.
     *
     * @param  res  to process
     *
     * @return  search item
     */
    protected SearchItem processLDAPIntermediateResponse(final LDAPIntermediateResponse res)
    {
      logger.trace("reading intermediate response: {}", res);

      ResponseControl[] respControls = null;
      if (res.getControls() != null && res.getControls().length > 0) {
        respControls = config.getControlProcessor().processResponseControls(res.getControls());
      }

      final org.ldaptive.intermediate.IntermediateResponse ir = IntermediateResponseFactory.createIntermediateResponse(
        res.getID(),
        res.getValue(),
        respControls,
        res.getMessageID());
      return new SearchItem(ir);
    }
  }


  /** Iterates over an ldap search queue. */
  protected static class SearchResultIterator
  {

    /** Queue to iterate over. */
    private final LDAPSearchQueue queue;

    /** Last response message received from the queue. */
    private LDAPMessage message;

    /** Response available after all messages have been received. */
    private LDAPResponse response;


    /**
     * Create a new ldap result iterator.
     *
     * @param  q  ldap search queue
     */
    public SearchResultIterator(final LDAPSearchQueue q)
    {
      queue = q;
    }


    /**
     * Returns the search queue used by this iterator.
     *
     * @return  ldap search queue
     */
    public LDAPSearchQueue getLDAPSearchQueue()
    {
      return queue;
    }


    /**
     * Returns whether the queue has another message to read.
     *
     * @return  whether the queue has another message to read
     *
     * @throws  LDAPException  if an error occurs reading the response
     */
    public boolean hasNext()
      throws LDAPException
    {
      if (response != null) {
        return false;
      }

      boolean more = false;
      message = queue.getResponse();
      if (message != null) {
        if (message instanceof LDAPSearchResult) {
          more = true;
        } else if (message instanceof LDAPSearchResultReference) {
          more = true;
        } else if (message instanceof LDAPIntermediateResponse) {
          more = true;
        } else {
          response = (LDAPResponse) message;
        }
      }
      return more;
    }


    /**
     * Returns the next message in the queue.
     *
     * @return  ldap message
     */
    public LDAPMessage next()
    {
      return message;
    }


    /**
     * Returns the search response. Available after all messages have been read from the queue.
     *
     * @return  ldap search response
     */
    public LDAPResponse getResponse()
    {
      return response;
    }
  }


  /** Async request to invoke abandons. */
  protected class JLdapAsyncRequest implements AsyncRequest
  {

    /** Message queue. */
    private final LDAPMessageQueue messageQueue;


    /**
     * Creates a new JLDAP async request.
     *
     * @param  queue  from an async operation
     */
    public JLdapAsyncRequest(final LDAPMessageQueue queue)
    {
      messageQueue = queue;
    }


    @Override
    public int getMessageId()
    {
      final int[] ids = messageQueue.getMessageIDs();
      if (ids == null || ids.length == 0) {
        return -1;
      }
      return ids[ids.length - 1];
    }


    @Override
    public void abandon()
      throws LdapException
    {
      try {
        LDAPConstraints constraints = connection.getConstraints();
        if (constraints == null) {
          constraints = new LDAPConstraints();
        }
        connection.abandon(messageQueue, constraints);
      } catch (LDAPException e) {
        processLDAPException(e);
      }
    }


    @Override
    public void abandon(final RequestControl[] controls)
      throws LdapException
    {
      try {
        LDAPConstraints constraints = connection.getConstraints();
        if (constraints == null) {
          constraints = new LDAPConstraints();
        }
        if (controls != null) {
          constraints.setControls(config.getControlProcessor().processRequestControls(controls));
        }
        connection.abandon(messageQueue, constraints);
      } catch (LDAPException e) {
        processLDAPException(e);
      }
    }
  }


  /** Allows the use of multiple unsolicited notification handlers per connection. */
  protected class AggregateUnsolicitedNotificationListener implements LDAPUnsolicitedNotificationListener
  {

    /** Listeners to receive unsolicited notifications. */
    private final List listeners = new ArrayList<>();


    /**
     * Adds an unsolicited notification listener to this handler.
     *
     * @param  listener  to receive unsolicited notifications
     */
    public void addUnsolicitedNotificationListener(final UnsolicitedNotificationListener listener)
    {
      synchronized (listeners) {
        listeners.add(listener);
      }
    }


    /**
     * Removes an unsolicited notification listener from this handler.
     *
     * @param  listener  to stop receiving unsolicited notifications
     */
    public void removeUnsolicitedNotificationListener(final UnsolicitedNotificationListener listener)
    {
      synchronized (listeners) {
        listeners.remove(listener);
      }
    }


    @Override
    public void messageReceived(final LDAPExtendedResponse extendedResponse)
    {
      logger.debug("Unsolicited notification received: {}", extendedResponse);
      synchronized (listeners) {
        final Response response = createResponse(null, null, extendedResponse);
        for (UnsolicitedNotificationListener listener : listeners) {
          listener.notificationReceived(extendedResponse.getID(), response);
        }
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy