001    /*
002     * Copyright 2007-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2015 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     */
021    package com.unboundid.ldap.sdk;
022    
023    
024    
025    import com.unboundid.util.ThreadSafety;
026    import com.unboundid.util.ThreadSafetyLevel;
027    
028    import static com.unboundid.ldap.sdk.LDAPMessages.*;
029    import 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)
041    public 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    }