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