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