001    /*
002     * Copyright 2008-2014 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2014 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.util;
022    
023    
024    
025    import java.io.Serializable;
026    import java.util.EnumSet;
027    import java.util.Properties;
028    import java.util.Set;
029    import java.util.StringTokenizer;
030    import java.util.logging.Level;
031    import java.util.logging.Logger;
032    
033    import com.unboundid.asn1.ASN1Buffer;
034    import com.unboundid.asn1.ASN1Element;
035    import com.unboundid.ldap.protocol.LDAPResponse;
036    import com.unboundid.ldap.sdk.DisconnectType;
037    import com.unboundid.ldap.sdk.Entry;
038    import com.unboundid.ldap.sdk.LDAPConnection;
039    import com.unboundid.ldap.sdk.LDAPRequest;
040    import com.unboundid.ldap.sdk.Version;
041    import com.unboundid.ldif.LDIFRecord;
042    
043    import static com.unboundid.util.StaticUtils.*;
044    
045    
046    
047    /**
048     * This class provides a means of enabling and configuring debugging in the LDAP
049     * SDK.
050     * <BR><BR>
051     * Access to debug information can be enabled through applications that use the
052     * SDK by calling the {@code Debug#setEnabled} methods, or it can also be
053     * enabled without any code changes through the use of system properties.  In
054     * particular, the {@code Debug#PROPERTY_DEBUG_ENABLED},
055     * {@code Debug#PROPERTY_DEBUG_LEVEL}, and {@code Debug#PROPERTY_DEBUG_TYPE}
056     * properties may be used to control debugging without the need to alter any
057     * code within the application that uses the SDK.
058     * <BR><BR>
059     * The LDAP SDK debugging subsystem uses the Java logging framework available
060     * through the {@code java.util.logging} package with a logger name of
061     * "{@code com.unboundid.ldap.sdk}".  The {@code Debug#getLogger} method may
062     * be used to access the logger instance used by the LDAP SDK.
063     * <BR><BR>
064     * <H2>Example</H2>
065     * The following example demonstrates the process that may be used to enable
066     * debugging within the LDAP SDK and write information about all messages with
067     * a {@code WARNING} level or higher to a specified file:
068     * <PRE>
069     * Debug.setEnabled(true);
070     * Logger logger = Debug.getLogger();
071     *
072     * FileHandler fileHandler = new FileHandler(logFilePath);
073     * fileHandler.setLevel(Level.WARNING);
074     * logger.addHandler(fileHandler);
075     * </PRE>
076     */
077    public final class Debug
078           implements Serializable
079    {
080      /**
081       * The name of the system property that will be used to enable debugging in
082       * the UnboundID LDAP SDK for Java.  The fully-qualified name for this
083       * property is "{@code com.unboundid.ldap.sdk.debug.enabled}".  If it is set,
084       * then it should have a value of either "true" or "false".
085       */
086      public static final String PROPERTY_DEBUG_ENABLED =
087           "com.unboundid.ldap.sdk.debug.enabled";
088    
089    
090    
091      /**
092       * The name of the system property that may be used to indicate whether stack
093       * trace information for the thread calling the debug method should be
094       * included in debug log messages.  The fully-qualified name for this property
095       * is "{@code com.unboundid.ldap.sdk.debug.includeStackTrace}".  If it is set,
096       * then it should have a value of either "true" or "false".
097       */
098      public static final String PROPERTY_INCLUDE_STACK_TRACE =
099           "com.unboundid.ldap.sdk.debug.includeStackTrace";
100    
101    
102    
103      /**
104       * The name of the system property that will be used to set the initial level
105       * for the debug logger.  The fully-qualified name for this property is
106       * "{@code com.unboundid.ldap.sdk.debug.level}".  If it is set, then it should
107       * be one of the strings "{@code SEVERE}", "{@code WARNING}", "{@code INFO}",
108       * "{@code CONFIG}", "{@code FINE}", "{@code FINER}", or "{@code FINEST}".
109       */
110      public static final String PROPERTY_DEBUG_LEVEL =
111           "com.unboundid.ldap.sdk.debug.level";
112    
113    
114    
115      /**
116       * The name of the system property that will be used to indicate that
117       * debugging should be enabled for specific types of messages.  The
118       * fully-qualified name for this property is
119       * "{@code com.unboundid.ldap.sdk.debug.type}". If it is set, then it should
120       * be a comma-delimited list of the names of the desired debug types.  See the
121       * {@code DebugType} enum for the available debug types.
122       */
123      public static final String PROPERTY_DEBUG_TYPE =
124           "com.unboundid.ldap.sdk.debug.type";
125    
126    
127    
128      /**
129       * The name that will be used for the Java logger that will actually handle
130       * the debug messages if debugging is enabled.
131       */
132      public static final String LOGGER_NAME = "com.unboundid.ldap.sdk";
133    
134    
135    
136      /**
137       * The logger that will be used to handle the debug messages if debugging is
138       * enabled.
139       */
140      private static final Logger logger = Logger.getLogger(LOGGER_NAME);
141    
142    
143    
144      /**
145       * The serial version UID for this serializable class.
146       */
147      private static final long serialVersionUID = -6079754380415146030L;
148    
149    
150    
151      // Indicates whether any debugging is currently enabled for the SDK.
152      private static boolean debugEnabled;
153    
154      // Indicates whether to capture a thread stack trace whenever a debug message
155      // is logged.
156      private static boolean includeStackTrace;
157    
158      // The set of debug types for which debugging is enabled.
159      private static EnumSet<DebugType> debugTypes;
160    
161    
162    
163      static
164      {
165        initialize(System.getProperties());
166      }
167    
168    
169    
170      /**
171       * Prevent this class from being instantiated.
172       */
173      private Debug()
174      {
175        // No implementation is required.
176      }
177    
178    
179    
180      /**
181       * Initializes this debugger with the default settings.  Debugging will be
182       * disabled, the set of debug types will include all types, and the debug
183       * level will be "ALL".
184       */
185      public static void initialize()
186      {
187        includeStackTrace = false;
188        debugEnabled      = false;
189        debugTypes        = EnumSet.allOf(DebugType.class);
190    
191        logger.setLevel(Level.ALL);
192      }
193    
194    
195    
196      /**
197       * Initializes this debugger with settings from the provided set of
198       * properties.  Any debug setting that isn't configured in the provided
199       * properties will be initialized with its default value.
200       *
201       * @param  properties  The set of properties to use to initialize this
202       *                     debugger.
203       */
204      public static void initialize(final Properties properties)
205      {
206        // First, apply the default values for the properties.
207        initialize();
208        if ((properties == null) || properties.isEmpty())
209        {
210          // No properties were provided, so we don't need to do anything.
211          return;
212        }
213    
214        final String enabledProp = properties.getProperty(PROPERTY_DEBUG_ENABLED);
215        if ((enabledProp != null) && (enabledProp.length() > 0))
216        {
217          if (enabledProp.equalsIgnoreCase("true"))
218          {
219            debugEnabled = true;
220          }
221          else if (enabledProp.equalsIgnoreCase("false"))
222          {
223            debugEnabled = false;
224          }
225          else
226          {
227            throw new IllegalArgumentException("Invalid value '" + enabledProp +
228                                               "' for property " +
229                                               PROPERTY_DEBUG_ENABLED +
230                                               ".  The value must be either " +
231                                               "'true' or 'false'.");
232          }
233        }
234    
235        final String stackProp =
236             properties.getProperty(PROPERTY_INCLUDE_STACK_TRACE);
237        if ((stackProp != null) && (stackProp.length() > 0))
238        {
239          if (stackProp.equalsIgnoreCase("true"))
240          {
241            includeStackTrace = true;
242          }
243          else if (stackProp.equalsIgnoreCase("false"))
244          {
245            includeStackTrace = false;
246          }
247          else
248          {
249            throw new IllegalArgumentException("Invalid value '" + stackProp +
250                                               "' for property " +
251                                               PROPERTY_INCLUDE_STACK_TRACE +
252                                               ".  The value must be either " +
253                                               "'true' or 'false'.");
254          }
255        }
256    
257        final String typesProp = properties.getProperty(PROPERTY_DEBUG_TYPE);
258        if ((typesProp != null) && (typesProp.length() > 0))
259        {
260          debugTypes = EnumSet.noneOf(DebugType.class);
261          final StringTokenizer t = new StringTokenizer(typesProp, ", ");
262          while (t.hasMoreTokens())
263          {
264            final String debugTypeName = t.nextToken();
265            final DebugType debugType = DebugType.forName(debugTypeName);
266            if (debugType == null)
267            {
268              // Throw a runtime exception to indicate that the debug type is
269              // invalid.
270              throw new IllegalArgumentException("Invalid value '" + debugTypeName +
271                          "' for property " + PROPERTY_DEBUG_TYPE +
272                          ".  Allowed values include:  " +
273                          DebugType.getTypeNameList() + '.');
274            }
275            else
276            {
277              debugTypes.add(debugType);
278            }
279          }
280        }
281    
282        final String levelProp = properties.getProperty(PROPERTY_DEBUG_LEVEL);
283        if ((levelProp != null) && (levelProp.length() > 0))
284        {
285          logger.setLevel(Level.parse(levelProp));
286        }
287      }
288    
289    
290    
291      /**
292       * Retrieves the logger that will be used to write the debug messages.
293       *
294       * @return  The logger that will be used to write the debug messages.
295       */
296      public static Logger getLogger()
297      {
298        return logger;
299      }
300    
301    
302    
303      /**
304       * Indicates whether any form of debugging is enabled.
305       *
306       * @return  {@code true} if debugging is enabled, or {@code false} if not.
307       */
308      public static boolean debugEnabled()
309      {
310        return debugEnabled;
311      }
312    
313    
314    
315      /**
316       * Indicates whether debugging is enabled for messages of the specified debug
317       * type.
318       *
319       * @param  debugType  The debug type for which to make the determination.
320       *
321       * @return  {@code true} if debugging is enabled for messages of the specified
322       *          debug type, or {@code false} if not.
323       */
324      public static boolean debugEnabled(final DebugType debugType)
325      {
326        return (debugEnabled && debugTypes.contains(debugType));
327      }
328    
329    
330    
331      /**
332       * Specifies whether debugging should be enabled.  If it should be, then it
333       * will be enabled for all debug types.
334       *
335       * @param  enabled  Specifies whether debugging should be enabled.
336       */
337      public static void setEnabled(final boolean enabled)
338      {
339        debugTypes   = EnumSet.allOf(DebugType.class);
340        debugEnabled = enabled;
341      }
342    
343    
344    
345      /**
346       * Specifies whether debugging should be enabled.  If it should be, then it
347       * will be enabled for all debug types in the provided set.
348       *
349       * @param  enabled  Specifies whether debugging should be enabled.
350       * @param  types    The set of debug types that should be enabled.  It may be
351       *                  {@code null} or empty to indicate that it should be for
352       *                  all debug types.
353       */
354      public static void setEnabled(final boolean enabled,
355                                    final Set<DebugType> types)
356      {
357        if ((types == null) || types.isEmpty())
358        {
359          debugTypes = EnumSet.allOf(DebugType.class);
360        }
361        else
362        {
363          debugTypes = EnumSet.copyOf(types);
364        }
365    
366        debugEnabled = enabled;
367      }
368    
369    
370    
371      /**
372       * Indicates whether log messages should include a stack trace of the thread
373       * that invoked the debug method.
374       *
375       * @return  {@code true} if log messages should include a stack trace of the
376       *          thread that invoked the debug method, or {@code false} if not.
377       */
378      public static boolean includeStackTrace()
379      {
380        return includeStackTrace;
381      }
382    
383    
384    
385      /**
386       * Specifies whether log messages should include a stack trace of the thread
387       * that invoked the debug method.
388       *
389       * @param  includeStackTrace  Indicates whether log messages should include a
390       *                            stack trace of the thread that invoked the debug
391       *                            method.
392       */
393      public static void setIncludeStackTrace(final boolean includeStackTrace)
394      {
395        Debug.includeStackTrace = includeStackTrace;
396      }
397    
398    
399    
400      /**
401       * Retrieves the set of debug types that will be used if debugging is enabled.
402       *
403       * @return  The set of debug types that will be used if debugging is enabled.
404       */
405      public static EnumSet<DebugType> getDebugTypes()
406      {
407        return debugTypes;
408      }
409    
410    
411    
412      /**
413       * Writes debug information about the provided exception, if appropriate.  If
414       * it is to be logged, then it will be sent to the underlying logger using the
415       * {@code WARNING} level.
416       *
417       * @param  t  The exception for which debug information should be written.
418       */
419      public static void debugException(final Throwable t)
420      {
421        if (debugEnabled && debugTypes.contains(DebugType.EXCEPTION))
422        {
423          debugException(Level.WARNING, t);
424        }
425      }
426    
427    
428    
429      /**
430       * Writes debug information about the provided exception, if appropriate.
431       *
432       * @param  l  The log level that should be used for the debug information.
433       * @param  t  The exception for which debug information should be written.
434       */
435      public static void debugException(final Level l, final Throwable t)
436      {
437        if (debugEnabled && debugTypes.contains(DebugType.EXCEPTION))
438        {
439          final StringBuilder buffer = new StringBuilder();
440          addCommonHeader(buffer, l);
441          buffer.append("caughtException=\"");
442          getStackTrace(t, buffer);
443          buffer.append('"');
444    
445          logger.log(l, buffer.toString(), t);
446        }
447      }
448    
449    
450    
451      /**
452       * Writes debug information to indicate that a connection has been
453       * established, if appropriate.  If it is to be logged, then it will be sent
454       * to the underlying logger using the {@code INFO} level.
455       *
456       * @param  h  The address of the server to which the connection was
457       *            established.
458       * @param  p  The port of the server to which the connection was established.
459       */
460      public static void debugConnect(final String h, final int p)
461      {
462        if (debugEnabled && debugTypes.contains(DebugType.CONNECT))
463        {
464          debugConnect(Level.INFO, h, p, null);
465        }
466      }
467    
468    
469    
470      /**
471       * Writes debug information to indicate that a connection has been
472       * established, if appropriate.
473       *
474       * @param  l  The log level that should be used for the debug information.
475       * @param  h  The address of the server to which the connection was
476       *            established.
477       * @param  p  The port of the server to which the connection was established.
478       */
479      public static void debugConnect(final Level l, final String h, final int p)
480      {
481        if (debugEnabled && debugTypes.contains(DebugType.CONNECT))
482        {
483          debugConnect(l, h, p, null);
484        }
485      }
486    
487    
488    
489      /**
490       * Writes debug information to indicate that a connection has been
491       * established, if appropriate.  If it is to be logged, then it will be sent
492       * to the underlying logger using the {@code INFO} level.
493       *
494       * @param  h  The address of the server to which the connection was
495       *            established.
496       * @param  p  The port of the server to which the connection was established.
497       * @param  c  The connection object for the connection that has been
498       *            established.  It may be {@code null} for historic reasons, but
499       *            should be non-{@code null} in new uses.
500       */
501      public static void debugConnect(final String h, final int p,
502                                      final LDAPConnection c)
503      {
504        if (debugEnabled && debugTypes.contains(DebugType.CONNECT))
505        {
506          debugConnect(Level.INFO, h, p, c);
507        }
508      }
509    
510    
511    
512      /**
513       * Writes debug information to indicate that a connection has been
514       * established, if appropriate.
515       *
516       * @param  l  The log level that should be used for the debug information.
517       * @param  h  The address of the server to which the connection was
518       *            established.
519       * @param  p  The port of the server to which the connection was established.
520       * @param  c  The connection object for the connection that has been
521       *            established.  It may be {@code null} for historic reasons, but
522       *            should be non-{@code null} in new uses.
523       */
524      public static void debugConnect(final Level l, final String h, final int p,
525                                      final LDAPConnection c)
526      {
527        if (debugEnabled && debugTypes.contains(DebugType.CONNECT))
528        {
529          final StringBuilder buffer = new StringBuilder();
530          addCommonHeader(buffer, l);
531          buffer.append("connectedTo=\"");
532          buffer.append(h);
533          buffer.append(':');
534          buffer.append(p);
535          buffer.append('"');
536    
537          if (c != null)
538          {
539            buffer.append(" connectionID=");
540            buffer.append(c.getConnectionID());
541    
542            final String connectionName = c.getConnectionName();
543            if (connectionName != null)
544            {
545              buffer.append(" connectionName=\"");
546              buffer.append(connectionName);
547              buffer.append('"');
548            }
549    
550            final String connectionPoolName = c.getConnectionPoolName();
551            if (connectionPoolName != null)
552            {
553              buffer.append(" connectionPoolName=\"");
554              buffer.append(connectionPoolName);
555              buffer.append('"');
556            }
557          }
558    
559          logger.log(l, buffer.toString());
560        }
561      }
562    
563    
564    
565      /**
566       * Writes debug information to indicate that a connection has been
567       * terminated, if appropriate.  If it is to be logged, then it will be sent
568       * to the underlying logger using the {@code INFO} level.
569       *
570       * @param  h  The address of the server to which the connection was
571       *            established.
572       * @param  p  The port of the server to which the connection was established.
573       * @param  t  The disconnect type.
574       * @param  m  The disconnect message, if available.
575       * @param  e  The disconnect cause, if available.
576       */
577      public static void debugDisconnect(final String h, final int p,
578                                         final DisconnectType t, final String m,
579                                         final Throwable e)
580      {
581        if (debugEnabled && debugTypes.contains(DebugType.CONNECT))
582        {
583          debugDisconnect(Level.INFO, h, p, null, t, m, e);
584        }
585      }
586    
587    
588    
589      /**
590       * Writes debug information to indicate that a connection has been
591       * terminated, if appropriate.
592       *
593       * @param  l  The log level that should be used for the debug information.
594       * @param  h  The address of the server to which the connection was
595       *            established.
596       * @param  p  The port of the server to which the connection was established.
597       * @param  t  The disconnect type.
598       * @param  m  The disconnect message, if available.
599       * @param  e  The disconnect cause, if available.
600       */
601      public static void debugDisconnect(final Level l, final String h, final int p,
602                                         final DisconnectType t, final String m,
603                                         final Throwable e)
604      {
605        if (debugEnabled && debugTypes.contains(DebugType.CONNECT))
606        {
607          debugDisconnect(l, h, p, null, t, m, e);
608        }
609      }
610    
611    
612    
613      /**
614       * Writes debug information to indicate that a connection has been
615       * terminated, if appropriate.  If it is to be logged, then it will be sent
616       * to the underlying logger using the {@code INFO} level.
617       *
618       * @param  h  The address of the server to which the connection was
619       *            established.
620       * @param  p  The port of the server to which the connection was established.
621       * @param  c  The connection object for the connection that has been closed.
622       *            It may be {@code null} for historic reasons, but should be
623       *            non-{@code null} in new uses.
624       * @param  t  The disconnect type.
625       * @param  m  The disconnect message, if available.
626       * @param  e  The disconnect cause, if available.
627       */
628      public static void debugDisconnect(final String h, final int p,
629                                         final LDAPConnection c,
630                                         final DisconnectType t, final String m,
631                                         final Throwable e)
632      {
633        if (debugEnabled && debugTypes.contains(DebugType.CONNECT))
634        {
635          debugDisconnect(Level.INFO, h, p, c, t, m, e);
636        }
637      }
638    
639    
640    
641      /**
642       * Writes debug information to indicate that a connection has been
643       * terminated, if appropriate.
644       *
645       * @param  l  The log level that should be used for the debug information.
646       * @param  h  The address of the server to which the connection was
647       *            established.
648       * @param  p  The port of the server to which the connection was established.
649       * @param  c  The connection object for the connection that has been closed.
650       *            It may be {@code null} for historic reasons, but should be
651       *            non-{@code null} in new uses.
652       * @param  t  The disconnect type.
653       * @param  m  The disconnect message, if available.
654       * @param  e  The disconnect cause, if available.
655       */
656      public static void debugDisconnect(final Level l, final String h, final int p,
657                                         final LDAPConnection c,
658                                         final DisconnectType t, final String m,
659                                         final Throwable e)
660      {
661        if (debugEnabled && debugTypes.contains(DebugType.CONNECT))
662        {
663          final StringBuilder buffer = new StringBuilder();
664          addCommonHeader(buffer, l);
665    
666          if (c != null)
667          {
668            buffer.append("connectionID=");
669            buffer.append(c.getConnectionID());
670    
671            final String connectionName = c.getConnectionName();
672            if (connectionName != null)
673            {
674              buffer.append(" connectionName=\"");
675              buffer.append(connectionName);
676              buffer.append('"');
677            }
678    
679            final String connectionPoolName = c.getConnectionPoolName();
680            if (connectionPoolName != null)
681            {
682              buffer.append(" connectionPoolName=\"");
683              buffer.append(connectionPoolName);
684              buffer.append('"');
685            }
686    
687            buffer.append(' ');
688          }
689    
690          buffer.append("disconnectedFrom=\"");
691          buffer.append(h);
692          buffer.append(':');
693          buffer.append(p);
694          buffer.append("\" disconnectType=\"");
695          buffer.append(t.name());
696          buffer.append('"');
697    
698          if (m != null)
699          {
700            buffer.append("\" disconnectMessage=\"");
701            buffer.append(m);
702            buffer.append('"');
703          }
704    
705          if (e != null)
706          {
707            buffer.append("\" disconnectCause=\"");
708            getStackTrace(e, buffer);
709            buffer.append('"');
710          }
711    
712          logger.log(l, buffer.toString(), c);
713        }
714      }
715    
716    
717    
718      /**
719       * Writes debug information about the provided request, if appropriate.  If
720       * it is to be logged, then it will be sent to the underlying logger using the
721       * {@code INFO} level.
722       *
723       * @param  r  The LDAP request for which debug information should be written.
724       */
725      public static void debugLDAPRequest(final LDAPRequest r)
726      {
727        if (debugEnabled && debugTypes.contains(DebugType.LDAP))
728        {
729          debugLDAPRequest(Level.INFO, r, -1, null);
730        }
731      }
732    
733    
734    
735      /**
736       * Writes debug information about the provided request, if appropriate.
737       *
738       * @param  l  The log level that should be used for the debug information.
739       * @param  r  The LDAP request for which debug information should be written.
740       */
741      public static void debugLDAPRequest(final Level l, final LDAPRequest r)
742      {
743        if (debugEnabled && debugTypes.contains(DebugType.LDAP))
744        {
745          debugLDAPRequest(l, r, -1, null);
746        }
747      }
748    
749    
750    
751      /**
752       * Writes debug information about the provided request, if appropriate.  If
753       * it is to be logged, then it will be sent to the underlying logger using the
754       * {@code INFO} level.
755       *
756       * @param  r  The LDAP request for which debug information should be written.
757       * @param  i  The message ID for the request that will be sent.  It may be
758       *            negative if no message ID is available.
759       * @param  c  The connection on which the request will be sent.  It may be
760       *            {@code null} for historic reasons, but should be
761       *            non-{@code null} in new uses.
762       */
763      public static void debugLDAPRequest(final LDAPRequest r, final int i,
764                                          final LDAPConnection c)
765      {
766        if (debugEnabled && debugTypes.contains(DebugType.LDAP))
767        {
768          debugLDAPRequest(Level.INFO, r, i, c);
769        }
770      }
771    
772    
773    
774      /**
775       * Writes debug information about the provided request, if appropriate.
776       *
777       * @param  l  The log level that should be used for the debug information.
778       * @param  r  The LDAP request for which debug information should be written.
779       * @param  i  The message ID for the request that will be sent.  It may be
780       *            negative if no message ID is available.
781       * @param  c  The connection on which the request will be sent.  It may be
782       *            {@code null} for historic reasons, but should be
783       *            non-{@code null} in new uses.
784       */
785      public static void debugLDAPRequest(final Level l, final LDAPRequest r,
786                                          final int i, final LDAPConnection c)
787      {
788        if (debugEnabled && debugTypes.contains(DebugType.LDAP))
789        {
790          final StringBuilder buffer = new StringBuilder();
791          addCommonHeader(buffer, l);
792    
793          if (c != null)
794          {
795            buffer.append("connectionID=");
796            buffer.append(c.getConnectionID());
797    
798            final String connectionName = c.getConnectionName();
799            if (connectionName != null)
800            {
801              buffer.append(" connectionName=\"");
802              buffer.append(connectionName);
803              buffer.append('"');
804            }
805    
806            final String connectionPoolName = c.getConnectionPoolName();
807            if (connectionPoolName != null)
808            {
809              buffer.append(" connectionPoolName=\"");
810              buffer.append(connectionPoolName);
811              buffer.append('"');
812            }
813    
814            buffer.append(" connectedTo=\"");
815            buffer.append(c.getConnectedAddress());
816            buffer.append(':');
817            buffer.append(c.getConnectedPort());
818            buffer.append("\" ");
819          }
820    
821          if (i >= 0)
822          {
823            buffer.append(" messageID=");
824            buffer.append(i);
825            buffer.append(' ');
826          }
827    
828          buffer.append("sendingLDAPRequest=\"");
829          r.toString(buffer);
830          buffer.append('"');
831    
832          logger.log(l,  buffer.toString());
833        }
834      }
835    
836    
837    
838      /**
839       * Writes debug information about the provided result, if appropriate.  If
840       * it is to be logged, then it will be sent to the underlying logger using the
841       * {@code INFO} level.
842       *
843       * @param  r  The result for which debug information should be written.
844       */
845      public static void debugLDAPResult(final LDAPResponse r)
846      {
847        if (debugEnabled && debugTypes.contains(DebugType.LDAP))
848        {
849          debugLDAPResult(Level.INFO, r, null);
850        }
851      }
852    
853    
854    
855      /**
856       * Writes debug information about the provided result, if appropriate.
857       *
858       * @param  l  The log level that should be used for the debug information.
859       * @param  r  The result for which debug information should be written.
860       */
861      public static void debugLDAPResult(final Level l, final LDAPResponse r)
862      {
863        if (debugEnabled && debugTypes.contains(DebugType.LDAP))
864        {
865          debugLDAPResult(l, r, null);
866        }
867      }
868    
869    
870    
871      /**
872       * Writes debug information about the provided result, if appropriate.  If
873       * it is to be logged, then it will be sent to the underlying logger using the
874       * {@code INFO} level.
875       *
876       * @param  r  The result for which debug information should be written.
877       * @param  c  The connection on which the response was received.  It may be
878       *            {@code null} for historic reasons, but should be
879       *            non-{@code null} in new uses.
880       */
881      public static void debugLDAPResult(final LDAPResponse r,
882                                         final LDAPConnection c)
883      {
884        if (debugEnabled && debugTypes.contains(DebugType.LDAP))
885        {
886          debugLDAPResult(Level.INFO, r, c);
887        }
888      }
889    
890    
891    
892      /**
893       * Writes debug information about the provided result, if appropriate.
894       *
895       * @param  l  The log level that should be used for the debug information.
896       * @param  r  The result for which debug information should be written.
897       * @param  c  The connection on which the response was received.  It may be
898       *            {@code null} for historic reasons, but should be
899       *            non-{@code null} in new uses.
900       */
901      public static void debugLDAPResult(final Level l, final LDAPResponse r,
902                                         final LDAPConnection c)
903      {
904        if (debugEnabled && debugTypes.contains(DebugType.LDAP))
905        {
906          final StringBuilder buffer = new StringBuilder();
907          addCommonHeader(buffer, l);
908    
909          if (c != null)
910          {
911            buffer.append("connectionID=");
912            buffer.append(c.getConnectionID());
913    
914            final String connectionName = c.getConnectionName();
915            if (connectionName != null)
916            {
917              buffer.append(" connectionName=\"");
918              buffer.append(connectionName);
919              buffer.append('"');
920            }
921    
922            final String connectionPoolName = c.getConnectionPoolName();
923            if (connectionPoolName != null)
924            {
925              buffer.append(" connectionPoolName=\"");
926              buffer.append(connectionPoolName);
927              buffer.append('"');
928            }
929    
930            buffer.append(" connectedTo=\"");
931            buffer.append(c.getConnectedAddress());
932            buffer.append(':');
933            buffer.append(c.getConnectedPort());
934            buffer.append("\" ");
935          }
936    
937          buffer.append("readLDAPResult=\"");
938          r.toString(buffer);
939          buffer.append('"');
940    
941          logger.log(l,  buffer.toString());
942        }
943      }
944    
945    
946    
947      /**
948       * Writes debug information about the provided ASN.1 element to be written,
949       * if appropriate.  If it is to be logged, then it will be sent to the
950       * underlying logger using the {@code INFO} level.
951       *
952       * @param  e  The ASN.1 element for which debug information should be written.
953       */
954      public static void debugASN1Write(final ASN1Element e)
955      {
956        if (debugEnabled && debugTypes.contains(DebugType.ASN1))
957        {
958          debugASN1Write(Level.INFO, e);
959        }
960      }
961    
962    
963    
964      /**
965       * Writes debug information about the provided ASN.1 element to be written,
966       * if appropriate.
967       *
968       * @param  l  The log level that should be used for the debug information.
969       * @param  e  The ASN.1 element for which debug information should be written.
970       */
971      public static void debugASN1Write(final Level l, final ASN1Element e)
972      {
973        if (debugEnabled && debugTypes.contains(DebugType.ASN1))
974        {
975          final StringBuilder buffer = new StringBuilder();
976          addCommonHeader(buffer, l);
977          buffer.append("writingASN1Element=\"");
978          e.toString(buffer);
979          buffer.append('"');
980    
981          logger.log(l, buffer.toString());
982        }
983      }
984    
985    
986    
987      /**
988       * Writes debug information about the provided ASN.1 element to be written,
989       * if appropriate.  If it is to be logged, then it will be sent to the
990       * underlying logger using the {@code INFO} level.
991       *
992       * @param  b  The ASN.1 buffer with the information to be written.
993       */
994      public static void debugASN1Write(final ASN1Buffer b)
995      {
996        if (debugEnabled && debugTypes.contains(DebugType.ASN1))
997        {
998          debugASN1Write(Level.INFO, b);
999        }
1000      }
1001    
1002    
1003    
1004      /**
1005       * Writes debug information about the provided ASN.1 element to be written,
1006       * if appropriate.
1007       *
1008       * @param  l  The log level that should be used for the debug information.
1009       * @param  b  The ASN1Buffer with the information to be written.
1010       */
1011      public static void debugASN1Write(final Level l, final ASN1Buffer b)
1012      {
1013        if (debugEnabled && debugTypes.contains(DebugType.ASN1))
1014        {
1015          final StringBuilder buffer = new StringBuilder();
1016          addCommonHeader(buffer, l);
1017          buffer.append("writingASN1Element=\"");
1018          toHex(b.toByteArray(), buffer);
1019          buffer.append('"');
1020    
1021          logger.log(l, buffer.toString());
1022        }
1023      }
1024    
1025    
1026    
1027      /**
1028       * Writes debug information about the provided ASN.1 element that was read, if
1029       * appropriate.  If it is to be logged, then it will be sent to the underlying
1030       * logger using the {@code INFO} level.
1031       *
1032       * @param  e  The ASN.1 element for which debug information should be written.
1033       */
1034      public static void debugASN1Read(final ASN1Element e)
1035      {
1036        if (debugEnabled && debugTypes.contains(DebugType.ASN1))
1037        {
1038          debugASN1Read(Level.INFO, e);
1039        }
1040      }
1041    
1042    
1043    
1044      /**
1045       * Writes debug information about the provided ASN.1 element that was read, if
1046       * appropriate.
1047       *
1048       * @param  l  The log level that should be used for the debug information.
1049       * @param  e  The ASN.1 element for which debug information should be written.
1050       */
1051      public static void debugASN1Read(final Level l, final ASN1Element e)
1052      {
1053        if (debugEnabled && debugTypes.contains(DebugType.ASN1))
1054        {
1055          final StringBuilder buffer = new StringBuilder();
1056          addCommonHeader(buffer, l);
1057          buffer.append("readASN1Element=\"");
1058          e.toString(buffer);
1059          buffer.append('"');
1060    
1061          logger.log(l, buffer.toString());
1062        }
1063      }
1064    
1065    
1066    
1067      /**
1068       * Writes debug information about the provided LDIF record to be written, if
1069       * if appropriate.  If it is to be logged, then it will be sent to the
1070       * underlying logger using the {@code INFO} level.
1071       *
1072       * @param  r  The LDIF record for which debug information should be written.
1073       */
1074      public static void debugLDIFWrite(final LDIFRecord r)
1075      {
1076        if (debugEnabled && debugTypes.contains(DebugType.LDIF))
1077        {
1078          debugLDIFWrite(Level.INFO, r);
1079        }
1080      }
1081    
1082    
1083    
1084      /**
1085       * Writes debug information about the provided LDIF record to be written, if
1086       * appropriate.
1087       *
1088       * @param  l  The log level that should be used for the debug information.
1089       * @param  r  The LDIF record for which debug information should be written.
1090       */
1091      public static void debugLDIFWrite(final Level l, final LDIFRecord r)
1092      {
1093        if (debugEnabled && debugTypes.contains(DebugType.LDIF))
1094        {
1095          final StringBuilder buffer = new StringBuilder();
1096          addCommonHeader(buffer, l);
1097          buffer.append("writingLDIFRecord=\"");
1098          r.toString(buffer);
1099          buffer.append('"');
1100    
1101          logger.log(l, buffer.toString());
1102        }
1103      }
1104    
1105    
1106    
1107      /**
1108       * Writes debug information about the provided record read from LDIF, if
1109       * appropriate.  If it is to be logged, then it will be sent to the underlying
1110       * logger using the {@code INFO} level.
1111       *
1112       * @param  r  The LDIF record for which debug information should be written.
1113       */
1114      public static void debugLDIFRead(final LDIFRecord r)
1115      {
1116        if (debugEnabled && debugTypes.contains(DebugType.LDIF))
1117        {
1118          debugLDIFRead(Level.INFO, r);
1119        }
1120      }
1121    
1122    
1123    
1124      /**
1125       * Writes debug information about the provided record read from LDIF, if
1126       * appropriate.
1127       *
1128       * @param  l  The log level that should be used for the debug information.
1129       * @param  r  The LDIF record for which debug information should be written.
1130       */
1131      public static void debugLDIFRead(final Level l, final LDIFRecord r)
1132      {
1133        if (debugEnabled && debugTypes.contains(DebugType.LDIF))
1134        {
1135          final StringBuilder buffer = new StringBuilder();
1136          addCommonHeader(buffer, l);
1137          buffer.append("readLDIFRecord=\"");
1138          r.toString(buffer);
1139          buffer.append('"');
1140    
1141          logger.log(l, buffer.toString());
1142        }
1143      }
1144    
1145    
1146    
1147      /**
1148       * Writes debug information about monitor entry parsing.  If it is to be
1149       * logged, then it will be sent to the underlying logger using the
1150       * {@code FINE} level.
1151       *
1152       * @param  e  The entry containing the monitor information being parsed.
1153       * @param  m  The message to be written to the debug logger.
1154       */
1155      public static void debugMonitor(final Entry e, final String m)
1156      {
1157        if (debugEnabled && debugTypes.contains(DebugType.MONITOR))
1158        {
1159          debugMonitor(Level.FINE, e, m);
1160        }
1161      }
1162    
1163    
1164    
1165      /**
1166       * Writes debug information about monitor entry parsing, if appropriate.
1167       *
1168       * @param  l  The log level that should be used for the debug information.
1169       * @param  e  The entry containing the monitor information being parsed.
1170       * @param  m  The message to be written to the debug logger.
1171       */
1172      public static void debugMonitor(final Level l, final Entry e, final String m)
1173      {
1174        if (debugEnabled && debugTypes.contains(DebugType.MONITOR))
1175        {
1176          final StringBuilder buffer = new StringBuilder();
1177          addCommonHeader(buffer, l);
1178          buffer.append("monitorEntryDN=\"");
1179          buffer.append(e.getDN());
1180          buffer.append("\" message=\"");
1181          buffer.append(m);
1182          buffer.append('"');
1183    
1184          logger.log(l, buffer.toString());
1185        }
1186      }
1187    
1188    
1189    
1190      /**
1191       * Writes debug information about a coding error detected in the use of the
1192       * LDAP SDK.  If it is to be logged, then it will be sent to the underlying
1193       * logger using the {@code SEVERE} level.
1194       *
1195       * @param  t  The {@code Throwable} object that was created and will be thrown
1196       *            as a result of the coding error.
1197       */
1198      public static void debugCodingError(final Throwable t)
1199      {
1200        if (debugEnabled && debugTypes.contains(DebugType.CODING_ERROR))
1201        {
1202          final StringBuilder buffer = new StringBuilder();
1203          addCommonHeader(buffer, Level.SEVERE);
1204          buffer.append("codingError=\"");
1205          getStackTrace(t, buffer);
1206          buffer.append('"');
1207    
1208          logger.log(Level.SEVERE, buffer.toString());
1209        }
1210      }
1211    
1212    
1213    
1214      /**
1215       * Writes a generic debug message, if appropriate.
1216       *
1217       * @param  l  The log level that should be used for the debug information.
1218       * @param  t  The debug type to use to determine whether to write the message.
1219       * @param  m  The message to be written.
1220       */
1221      public static void debug(final Level l, final DebugType t, final String m)
1222      {
1223        if (debugEnabled && debugTypes.contains(t))
1224        {
1225          final StringBuilder buffer = new StringBuilder();
1226          addCommonHeader(buffer, l);
1227          buffer.append("message=\"");
1228          buffer.append(m);
1229          buffer.append('"');
1230    
1231          logger.log(l, buffer.toString());
1232        }
1233      }
1234    
1235    
1236    
1237      /**
1238       * Writes a generic debug message, if appropriate.
1239       *
1240       * @param  l  The log level that should be used for the debug information.
1241       * @param  t  The debug type to use to determine whether to write the message.
1242       * @param  m  The message to be written.
1243       * @param  e  An exception to include with the log message.
1244       */
1245      public static void debug(final Level l, final DebugType t, final String m,
1246                               final Throwable e)
1247      {
1248        if (debugEnabled && debugTypes.contains(t))
1249        {
1250          final StringBuilder buffer = new StringBuilder();
1251          addCommonHeader(buffer, l);
1252          buffer.append("message=\"");
1253          buffer.append(m);
1254          buffer.append('"');
1255          buffer.append(" exception=\"");
1256          getStackTrace(e, buffer);
1257          buffer.append('"');
1258    
1259          logger.log(l, buffer.toString(), e);
1260        }
1261      }
1262    
1263    
1264    
1265      /**
1266       * Writes common header information to the provided buffer.  It will include
1267       * the thread ID, name, and caller stack trace (optional), and it will be
1268       * followed by a trailing space.
1269       *
1270       * @param  buffer  The buffer to which the information should be appended.
1271       * @param  level   The log level for the message that will be written.
1272       */
1273      private static void addCommonHeader(final StringBuilder buffer,
1274                                          final Level level)
1275      {
1276        buffer.append("level=\"");
1277        buffer.append(level.getName());
1278        buffer.append("\" threadID=");
1279        buffer.append(Thread.currentThread().getId());
1280        buffer.append(" threadName=\"");
1281        buffer.append(Thread.currentThread().getName());
1282    
1283        if (includeStackTrace)
1284        {
1285          buffer.append("\" calledFrom=\"");
1286    
1287          boolean appended   = false;
1288          boolean foundDebug = false;
1289          for (final StackTraceElement e : Thread.currentThread().getStackTrace())
1290          {
1291            final String className = e.getClassName();
1292            if (className.equals(Debug.class.getName()))
1293            {
1294              foundDebug = true;
1295            }
1296            else if (foundDebug)
1297            {
1298              if (appended)
1299              {
1300                buffer.append(" / ");
1301              }
1302              appended = true;
1303    
1304              buffer.append(e.getMethodName());
1305              buffer.append('(');
1306              buffer.append(e.getFileName());
1307    
1308              final int lineNumber = e.getLineNumber();
1309              if (lineNumber > 0)
1310              {
1311                buffer.append(':');
1312                buffer.append(lineNumber);
1313              }
1314              else if (e.isNativeMethod())
1315              {
1316                buffer.append(":native");
1317              }
1318    
1319              buffer.append(')');
1320            }
1321          }
1322        }
1323    
1324        buffer.append("\" revision=");
1325        buffer.append(Version.REVISION_NUMBER);
1326        buffer.append(' ');
1327      }
1328    }