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