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