001/*
002 * Copyright 2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2018 Ping Identity Corporation
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.ldap.sdk;
022
023
024
025import com.unboundid.util.Debug;
026import com.unboundid.util.ThreadSafety;
027import com.unboundid.util.ThreadSafetyLevel;
028
029
030
031/**
032 * This class provides an implementation of a referral connector that will
033 * retain the exception encountered on the last attempt to establish a
034 * connection for the purpose of following a referral.
035 * <BR><BR>
036 * Note that although this class is technically safe to be used concurrently by
037 * multiple threads in that it won't result in a deadlock or concurrent
038 * modification exception or any other kind of obvious failure, it only retains
039 * a single exception, and only from the last attempt made to establish a
040 * connection for the purpose of following a referral.  If multiple threads try
041 * to use the same instance of this connector concurrently, a call to the
042 * {@link #getExceptionFromLastConnectAttempt()} method may return the result
043 * from the last attempt made on another thread.  It is therefore recommended
044 * that this connector only be used in contexts where it can be safely assumed
045 * that it will not be used concurrently across multiple threads.  For example,
046 * if a connection is not expected to be concurrently shared by multiple
047 * threads, then it may be desirable to use the
048 * {@link LDAPConnection#setReferralConnector(ReferralConnector)} to set a
049 * different instance of this connector for each connection.  Alternately, the
050 * {@link LDAPRequest#setReferralConnector(ReferralConnector)} method may be
051 * used to specify a connector that should be used for an individual request.
052 */
053@ThreadSafety(level=ThreadSafetyLevel.MOSTLY_NOT_THREADSAFE)
054public final class RetainConnectExceptionReferralConnector
055       implements ReferralConnector
056{
057  // The wrapped referral connector that will actually be used to establish the
058  // connection.
059  private final ReferralConnector wrappedReferralConnector;
060
061  // The exception caught in the last attempt to establish a connection for the
062  // purpose of following a referral.
063  private volatile LDAPException connectExceptionFromLastAttempt;
064
065
066
067  /**
068   * Creates a new instance of this referral connector that will use the
069   * connection's default referral handler to actually attempt to establish a
070   * connection.
071   */
072  public RetainConnectExceptionReferralConnector()
073  {
074    this(null);
075  }
076
077
078
079  /**
080   * Creates a new instance of this referral connector that will use the
081   * provided connector to actually attempt to establish a connection.
082   *
083   * @param  wrappedReferralConnector  The referral connector that will be used
084   *                                   to actually attempt to establish a
085   *                                   connection for the purpose of following a
086   *                                   referral.  This may be {@code null} to
087   *                                   use the default referral connector for
088   *                                   the connection on which the referral was
089   *                                   received.
090   */
091  public RetainConnectExceptionReferralConnector(
092              final ReferralConnector wrappedReferralConnector)
093  {
094    this.wrappedReferralConnector = wrappedReferralConnector;
095
096    connectExceptionFromLastAttempt = null;
097  }
098
099
100
101  /**
102   * Retrieves the exception that was caught in the last attempt to establish a
103   * connection for the purpose of following a referral, if any.
104   *
105   * @return  The exception that was caught in the last attempt to establish a
106   *          connection for the purpose of following a referral, or
107   *          {@code null} if the last connection attempt was successful or if
108   *          there have not yet been any connection attempts.
109   */
110  public LDAPException getExceptionFromLastConnectAttempt()
111  {
112    return connectExceptionFromLastAttempt;
113  }
114
115
116
117  /**
118   * {@inheritDoc}
119   */
120  @Override()
121  public LDAPConnection getReferralConnection(final LDAPURL referralURL,
122                                              final LDAPConnection connection)
123                 throws LDAPException
124  {
125    final ReferralConnector connector;
126    if (wrappedReferralConnector == null)
127    {
128      connector = connection.getReferralConnector();
129    }
130    else
131    {
132      connector = wrappedReferralConnector;
133    }
134
135    LDAPException connectException = null;
136    try
137    {
138      return connector.getReferralConnection(referralURL, connection);
139    }
140    catch (final LDAPException e)
141    {
142      Debug.debugException(e);
143      connectException = e;
144      throw e;
145    }
146    finally
147    {
148      connectExceptionFromLastAttempt = connectException;
149    }
150  }
151}