001/*
002 * Copyright 2007-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2007-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) 2007-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.NotNull;
041import com.unboundid.util.Nullable;
042import com.unboundid.util.StaticUtils;
043import com.unboundid.util.ThreadSafety;
044import com.unboundid.util.ThreadSafetyLevel;
045
046import static com.unboundid.ldap.sdk.LDAPMessages.*;
047
048
049
050/**
051 * This enum defines a set of disconnect types that may be used to provide
052 * general information about the reason that an {@link LDAPConnection} was
053 * disconnected.  Note that additional disconnect types may be added in the
054 * future, so any decision made based on a disconnect type should account for
055 * the possibility of previously-undefined disconnect types.
056 */
057@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
058public enum DisconnectType
059{
060  /**
061   * The connection was closed as a result of an unbind request sent by the
062   * client.
063   */
064  UNBIND(INFO_DISCONNECT_TYPE_UNBIND.get(), ResultCode.LOCAL_ERROR),
065
066
067
068  /**
069   * The connection was closed at the request of the client, but without first
070   * sending an unbind request.
071   */
072  CLOSED_WITHOUT_UNBIND(INFO_DISCONNECT_TYPE_CLOSED_WITHOUT_UNBIND.get(),
073       ResultCode.LOCAL_ERROR),
074
075
076
077  /**
078   * The connection was closed because a bind performed as part of the
079   * creation did not complete successfully.
080   */
081  BIND_FAILED(INFO_DISCONNECT_TYPE_BIND_FAILED.get(),
082       ResultCode.CONNECT_ERROR),
083
084
085
086  /**
087   * The connection was closed because it is going to be re-established.
088   */
089  RECONNECT(INFO_DISCONNECT_TYPE_RECONNECT.get(), ResultCode.SERVER_DOWN),
090
091
092
093  /**
094   * The connection was closed because it had been a temporary connection
095   * created for following a referral and was no longer needed.
096   */
097  REFERRAL(INFO_DISCONNECT_TYPE_REFERRAL.get(), ResultCode.LOCAL_ERROR),
098
099
100
101  /**
102   * The connection was closed by the server, and a notice of disconnection
103   * unsolicited notification was provided.
104   */
105  SERVER_CLOSED_WITH_NOTICE(
106       INFO_DISCONNECT_TYPE_SERVER_CLOSED_WITH_NOTICE.get(),
107       ResultCode.SERVER_DOWN),
108
109
110
111  /**
112   * The connection was closed by the server without a notice of disconnection.
113   */
114  SERVER_CLOSED_WITHOUT_NOTICE(
115       INFO_DISCONNECT_TYPE_SERVER_CLOSED_WITHOUT_NOTICE.get(),
116       ResultCode.SERVER_DOWN),
117
118
119
120  /**
121   * The connection was closed because an I/O problem was encountered while
122   * trying to communicate with the server.
123   */
124  IO_ERROR(INFO_DISCONNECT_TYPE_IO_ERROR.get(), ResultCode.SERVER_DOWN),
125
126
127
128  /**
129   * The connection was closed because an error occurred while trying to decode
130   * data from the server.
131   */
132  DECODE_ERROR(INFO_DISCONNECT_TYPE_DECODE_ERROR.get(),
133       ResultCode.DECODING_ERROR),
134
135
136
137  /**
138   * The connection was closed because an unexpected error occurred within the
139   * LDAP SDK.
140   */
141  LOCAL_ERROR(INFO_DISCONNECT_TYPE_LOCAL_ERROR.get(), ResultCode.LOCAL_ERROR),
142
143
144
145  /**
146   * The connection was closed because a problem was encountered while
147   * negotiating a security layer with the server.
148   */
149  SECURITY_PROBLEM(INFO_DISCONNECT_TYPE_SECURITY_PROBLEM.get(),
150       ResultCode.LOCAL_ERROR),
151
152
153
154  /**
155   * The connection was closed because it was part of a connection pool that
156   * was closed.
157   */
158  POOL_CLOSED(INFO_DISCONNECT_TYPE_POOL_CLOSED.get(), ResultCode.LOCAL_ERROR),
159
160
161
162  /**
163   * The connection was closed because it was part of a connection pool that
164   * was being initialized and a failure occurred while attempting to create
165   * another connection as part of the pool.
166   */
167  POOL_CREATION_FAILURE(INFO_DISCONNECT_TYPE_POOL_CREATION_FAILURE.get(),
168       ResultCode.CONNECT_ERROR),
169
170
171
172  /**
173   * The connection was closed because it was part of a connection pool and had
174   * been classified as defunct.
175   */
176  POOLED_CONNECTION_DEFUNCT(
177       INFO_DISCONNECT_TYPE_POOLED_CONNECTION_DEFUNCT.get(),
178       ResultCode.SERVER_DOWN),
179
180
181
182  /**
183   * The connection was closed because it was part of a connection pool and the
184   * connection had been established for longer than the maximum connection
185   * age for the pool.
186   */
187  POOLED_CONNECTION_EXPIRED(
188       INFO_DISCONNECT_TYPE_POOLED_CONNECTION_EXPIRED.get(),
189       ResultCode.LOCAL_ERROR),
190
191
192
193  /**
194   * The connection was closed because it was part of a connection pool and was
195   * no longer needed.
196   */
197  POOLED_CONNECTION_UNNEEDED(
198       INFO_DISCONNECT_TYPE_POOLED_CONNECTION_UNNEEDED.get(),
199       ResultCode.LOCAL_ERROR),
200
201
202
203  /**
204   * The reason for the disconnect is not known.  This generally indicates a
205   * problem with inappropriate instrumentation in the LDAP SDK.
206   */
207  UNKNOWN(INFO_DISCONNECT_TYPE_UNKNOWN.get(), ResultCode.LOCAL_ERROR),
208
209
210
211  /**
212   * The connection was closed by a finalizer in the LDAP SDK, which indicates
213   * that it was not properly closed by the application that had been using
214   * it.
215   */
216  CLOSED_BY_FINALIZER(INFO_DISCONNECT_TYPE_CLOSED_BY_FINALIZER.get(),
217       ResultCode.LOCAL_ERROR),
218
219
220
221  /**
222   * The connection was closed for a reason that does not fit any other
223   * defined disconnect type.
224   */
225  OTHER(INFO_DISCONNECT_TYPE_OTHER.get(), ResultCode.LOCAL_ERROR);
226
227
228
229  // The result code most closely associated with this disconnect type.
230  @NotNull private final ResultCode resultCode;
231
232  // A description for this disconnect type.
233  @NotNull private final String description;
234
235
236
237  /**
238   * Creates a new disconnect type with the specified description.
239   *
240   * @param  description  The description for this disconnect type.
241   * @param  resultCode   The result code most closely associated with this
242   *                      disconnect type.
243   */
244  DisconnectType(@NotNull final String description,
245                 @NotNull final ResultCode resultCode)
246  {
247    this.description = description;
248    this.resultCode  = resultCode;
249  }
250
251
252
253  /**
254   * Retrieves the description for this disconnect type.
255   *
256   * @return  The description for this disconnect type.
257   */
258  @NotNull()
259  public String getDescription()
260  {
261    return description;
262  }
263
264
265
266  /**
267   * Retrieves the result code most closely associated with this disconnect
268   * type.
269   *
270   * @return  The result code most closely associated with this disconnect type.
271   */
272  @NotNull()
273  public ResultCode getResultCode()
274  {
275    return resultCode;
276  }
277
278
279
280  /**
281   * Retrieves the disconnect type with the specified name.
282   *
283   * @param  name  The name of the disconnect type to retrieve.
284   *
285   * @return  The requested change type, or {@code null} if no such
286   *          disconnect type is defined.
287   */
288  @Nullable()
289  public static DisconnectType forName(@NotNull final String name)
290  {
291    switch (StaticUtils.toLowerCase(name))
292    {
293      case "unbind":
294        return UNBIND;
295      case "closedwithoutunbind":
296      case "closed-without-unbind":
297      case "closed_without_unbind":
298        return CLOSED_WITHOUT_UNBIND;
299      case "bindfailed":
300      case "bind-failed":
301      case "bind_failed":
302        return BIND_FAILED;
303      case "reconnect":
304        return RECONNECT;
305      case "referral":
306        return REFERRAL;
307      case "serverclosedwithnotice":
308      case "server-closed-with-notice":
309      case "server_closed_with_notice":
310        return SERVER_CLOSED_WITH_NOTICE;
311      case "serverclosedwithoutnotice":
312      case "server-closed-without-notice":
313      case "server_closed_without_notice":
314        return SERVER_CLOSED_WITHOUT_NOTICE;
315      case "ioerror":
316      case "io-error":
317      case "io_error":
318        return IO_ERROR;
319      case "decodeerror":
320      case "decode-error":
321      case "decode_error":
322        return DECODE_ERROR;
323      case "localerror":
324      case "local-error":
325      case "local_error":
326        return LOCAL_ERROR;
327      case "securityproblem":
328      case "security-problem":
329      case "security_problem":
330        return SECURITY_PROBLEM;
331      case "poolclosed":
332      case "pool-closed":
333      case "pool_closed":
334        return POOL_CLOSED;
335      case "poolcreationfailure":
336      case "pool-creation-failure":
337      case "pool_creation_failure":
338        return POOL_CREATION_FAILURE;
339      case "pooledconnectiondefunct":
340      case "pooled-connection-defunct":
341      case "pooled_connection_defunct":
342        return POOLED_CONNECTION_DEFUNCT;
343      case "pooledconnectionexpired":
344      case "pooled-connection-expired":
345      case "pooled_connection_expired":
346        return POOLED_CONNECTION_EXPIRED;
347      case "pooledconnectionunneeded":
348      case "pooled-connection-unneeded":
349      case "pooled_connection_unneeded":
350        return POOLED_CONNECTION_UNNEEDED;
351      case "unknown":
352        return UNKNOWN;
353      case "closedbyfinalizer":
354      case "closed-by-finalizer":
355      case "closed_by_finalizer":
356        return CLOSED_BY_FINALIZER;
357      case "other":
358        return OTHER;
359      default:
360        return null;
361    }
362  }
363
364
365
366  /**
367   * Indicates whether the provided disconnect type is likely one that is
368   * expected in some way.  This includes the following:
369   * <UL>
370   *   <LI>Connections closed by the application.</LI>
371   *   <LI>Connections which are managed as part of a connection pool.</LI>
372   *   <LI>Temporary connections created for following a referral.</LI>
373   *   <LI>Connections which are being closed by the SDK so they can be
374   *       re-established.</LI>
375   *   <LI>Connections that were not properly closed by the application but are
376   *       no longer in use and are being closed by a finalizer.</LI>
377   * </UL>
378   *
379   * @param  disconnectType  The disconnect type for which to make the
380   *                         determination.
381   *
382   * @return  {@code true} if the connection is one that can be classified as
383   *          expected and there is likely nothing that a disconnect handler
384   *          needs to do to handle it, or {@code false} if not.
385   */
386  public static boolean isExpected(@NotNull final DisconnectType disconnectType)
387  {
388    switch (disconnectType)
389    {
390      case UNBIND:
391      case CLOSED_WITHOUT_UNBIND:
392      case RECONNECT:
393      case REFERRAL:
394      case POOL_CLOSED:
395      case POOLED_CONNECTION_DEFUNCT:
396      case POOLED_CONNECTION_EXPIRED:
397      case POOLED_CONNECTION_UNNEEDED:
398      case CLOSED_BY_FINALIZER:
399        return true;
400      default:
401        return false;
402    }
403  }
404
405
406
407  /**
408   * Retrieves a string representation for this disconnect type.
409   *
410   * @return  A string representation for this disconnect type.
411   */
412  @Override()
413  @NotNull()
414  public String toString()
415  {
416    final StringBuilder buffer = new StringBuilder();
417    toString(buffer);
418    return buffer.toString();
419  }
420
421
422
423  /**
424   * Appends a string representation of this disconnect type to the provided
425   * buffer.
426   *
427   * @param  buffer  The buffer to which the string representation should be
428   *                 appended.
429   */
430  public void toString(@NotNull final StringBuilder buffer)
431  {
432    buffer.append("DisconnectType(name='");
433    buffer.append(name());
434    buffer.append("', resultCode='");
435    buffer.append(resultCode);
436    buffer.append("', description='");
437    buffer.append(description);
438    buffer.append("')");
439  }
440}