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