001    /*
002     * Copyright 2010-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2010-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.listener;
022    
023    
024    
025    import java.net.Socket;
026    import java.util.Arrays;
027    import java.util.List;
028    import java.util.logging.Handler;
029    import java.util.logging.Level;
030    import java.util.logging.LogRecord;
031    
032    import com.unboundid.asn1.ASN1OctetString;
033    import com.unboundid.ldap.protocol.AbandonRequestProtocolOp;
034    import com.unboundid.ldap.protocol.AddRequestProtocolOp;
035    import com.unboundid.ldap.protocol.AddResponseProtocolOp;
036    import com.unboundid.ldap.protocol.BindRequestProtocolOp;
037    import com.unboundid.ldap.protocol.BindResponseProtocolOp;
038    import com.unboundid.ldap.protocol.CompareRequestProtocolOp;
039    import com.unboundid.ldap.protocol.CompareResponseProtocolOp;
040    import com.unboundid.ldap.protocol.DeleteRequestProtocolOp;
041    import com.unboundid.ldap.protocol.DeleteResponseProtocolOp;
042    import com.unboundid.ldap.protocol.ExtendedRequestProtocolOp;
043    import com.unboundid.ldap.protocol.ExtendedResponseProtocolOp;
044    import com.unboundid.ldap.protocol.IntermediateResponseProtocolOp;
045    import com.unboundid.ldap.protocol.LDAPMessage;
046    import com.unboundid.ldap.protocol.ModifyRequestProtocolOp;
047    import com.unboundid.ldap.protocol.ModifyResponseProtocolOp;
048    import com.unboundid.ldap.protocol.ModifyDNRequestProtocolOp;
049    import com.unboundid.ldap.protocol.ModifyDNResponseProtocolOp;
050    import com.unboundid.ldap.protocol.SearchRequestProtocolOp;
051    import com.unboundid.ldap.protocol.SearchResultDoneProtocolOp;
052    import com.unboundid.ldap.protocol.SearchResultEntryProtocolOp;
053    import com.unboundid.ldap.protocol.SearchResultReferenceProtocolOp;
054    import com.unboundid.ldap.protocol.UnbindRequestProtocolOp;
055    import com.unboundid.ldap.sdk.Control;
056    import com.unboundid.ldap.sdk.Entry;
057    import com.unboundid.ldap.sdk.LDAPException;
058    import com.unboundid.ldap.sdk.ResultCode;
059    import com.unboundid.ldif.LDIFModifyChangeRecord;
060    import com.unboundid.util.NotMutable;
061    import com.unboundid.util.ObjectPair;
062    import com.unboundid.util.ThreadSafety;
063    import com.unboundid.util.ThreadSafetyLevel;
064    import com.unboundid.util.Validator;
065    
066    import static com.unboundid.util.StaticUtils.*;
067    
068    
069    
070    /**
071     * This class provides a request handler that may be used to write detailed
072     * information about the contents of all requests and responses that pass
073     * through it.  It will be also be associated with another request handler that
074     * will actually be used to handle the request.
075     */
076    @NotMutable()
077    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
078    public final class LDAPDebuggerRequestHandler
079           extends LDAPListenerRequestHandler
080           implements IntermediateResponseTransformer, SearchEntryTransformer,
081                      SearchReferenceTransformer
082    {
083      /**
084       * The thread-local buffers that will be used to hold the log messages as they
085       * are being generated.
086       */
087      private static final ThreadLocal<StringBuilder> BUFFERS =
088           new ThreadLocal<StringBuilder>();
089    
090    
091    
092      // The log handler that will be used to log the messages.
093      private final Handler logHandler;
094    
095      // The request handler that actually will be used to process any requests
096      // received.
097      private final LDAPListenerRequestHandler requestHandler;
098    
099      // The header string that will be used before each message.
100      private final String headerString;
101    
102    
103    
104      /**
105       * Creates a new LDAP debugger request handler that will write detailed
106       * information about the contents of all requests and responses that pass
107       * through it using the provided log handler, and will process client requests
108       * using the provided request handler.
109       *
110       * @param  logHandler      The log handler that will be used to write detailed
111       *                         information about requests and responses.  Note
112       *                         that all messages will be logged at the INFO level.
113       *                         It must not be {@code null}.  Note that the log
114       *                         handler will not be automatically closed when the
115       *                         associated listener is shut down.
116       * @param  requestHandler  The request handler that will actually be used to
117       *                         process any requests received.  It must not be
118       *                         {@code null}.
119       */
120      public LDAPDebuggerRequestHandler(final Handler logHandler,
121                  final LDAPListenerRequestHandler requestHandler)
122      {
123        Validator.ensureNotNull(logHandler, requestHandler);
124    
125        this.logHandler     = logHandler;
126        this.requestHandler = requestHandler;
127    
128        headerString = null;
129      }
130    
131    
132    
133      /**
134       * Creates a new LDAP debugger request handler that will write detailed
135       * information about the contents of all requests and responses that pass
136       * through it using the provided log handler, and will process client requests
137       * using the provided request handler.
138       *
139       * @param  logHandler      The log handler that will be used to write detailed
140       *                         information about requests and responses.  Note
141       *                         that all messages will be logged at the INFO level.
142       *                         It must not be {@code null}.
143       * @param  requestHandler  The request handler that will actually be used to
144       *                         process any requests received.  It must not be
145       *                         {@code null}.
146       * @param  headerString    The string that should be given as the first line
147       *                         of every log message.
148       */
149      private LDAPDebuggerRequestHandler(final Handler logHandler,
150                   final LDAPListenerRequestHandler requestHandler,
151                   final String headerString)
152      {
153        Validator.ensureNotNull(logHandler, requestHandler);
154    
155        this.logHandler     = logHandler;
156        this.requestHandler = requestHandler;
157        this.headerString    = headerString;
158      }
159    
160    
161    
162      /**
163       * {@inheritDoc}
164       */
165      @Override()
166      public LDAPDebuggerRequestHandler newInstance(
167                  final LDAPListenerClientConnection connection)
168             throws LDAPException
169      {
170        final StringBuilder b = getBuffer();
171        final Socket s = connection.getSocket();
172        b.append("conn=");
173        b.append(connection.getConnectionID());
174        b.append(" from=\"");
175        b.append(s.getInetAddress().getHostAddress());
176        b.append(':');
177        b.append(s.getPort());
178        b.append("\" to=\"");
179        b.append(s.getLocalAddress().getHostAddress());
180        b.append(':');
181        b.append(s.getLocalPort());
182        b.append('"');
183        b.append(EOL);
184    
185        final String header = b.toString();
186    
187        final LDAPDebuggerRequestHandler h = new LDAPDebuggerRequestHandler(
188             logHandler, requestHandler.newInstance(connection), header);
189    
190        connection.addIntermediateResponseTransformer(h);
191        connection.addSearchEntryTransformer(h);
192        connection.addSearchReferenceTransformer(h);
193    
194        logHandler.publish(new LogRecord(Level.INFO, "CONNECT " + header));
195    
196        return h;
197      }
198    
199    
200    
201      /**
202       * {@inheritDoc}
203       */
204      @Override()
205      public void closeInstance()
206      {
207        final StringBuilder b = getBuffer();
208        b.append("DISCONNECT ");
209        b.append(headerString);
210    
211        logHandler.publish(new LogRecord(Level.INFO, b.toString()));
212    
213        requestHandler.closeInstance();
214      }
215    
216    
217    
218      /**
219       * {@inheritDoc}
220       */
221      @Override()
222      public void processAbandonRequest(final int messageID,
223                                        final AbandonRequestProtocolOp request,
224                                        final List<Control> controls)
225      {
226        final StringBuilder b = getBuffer();
227        appendHeader(b, messageID);
228    
229        b.append("     Abandon Request Protocol Op:").append(EOL);
230        b.append("          ID to Abandon:  ").append(request.getIDToAbandon()).
231             append(EOL);
232    
233        appendControls(b, controls);
234        logHandler.publish(new LogRecord(Level.INFO, b.toString()));
235    
236        requestHandler.processAbandonRequest(messageID, request, controls);
237      }
238    
239    
240    
241      /**
242       * {@inheritDoc}
243       */
244      @Override()
245      public LDAPMessage processAddRequest(final int messageID,
246                                           final AddRequestProtocolOp request,
247                                           final List<Control> controls)
248      {
249        final StringBuilder b = getBuffer();
250        appendHeader(b, messageID);
251    
252        b.append("     Add Request Protocol Op:").append(EOL);
253    
254        final Entry e = new Entry(request.getDN(), request.getAttributes());
255        final String[] ldifLines = e.toLDIF(80);
256        for (final String line : ldifLines)
257        {
258          b.append("          ").append(line).append(EOL);
259        }
260    
261        appendControls(b, controls);
262        logHandler.publish(new LogRecord(Level.INFO, b.toString()));
263    
264        final LDAPMessage responseMessage = requestHandler.processAddRequest(
265             messageID, request, controls);
266    
267        b.setLength(0);
268        appendHeader(b, responseMessage.getMessageID());
269        b.append("     Add Response Protocol Op:").append(EOL);
270    
271        final AddResponseProtocolOp protocolOp =
272             responseMessage.getAddResponseProtocolOp();
273        appendResponse(b, protocolOp.getResultCode(),
274             protocolOp.getDiagnosticMessage(),
275             protocolOp.getMatchedDN(), protocolOp.getReferralURLs());
276    
277        appendControls(b, responseMessage.getControls());
278        logHandler.publish(new LogRecord(Level.INFO, b.toString()));
279    
280        return responseMessage;
281      }
282    
283    
284    
285      /**
286       * {@inheritDoc}
287       */
288      @Override()
289      public LDAPMessage processBindRequest(final int messageID,
290                                            final BindRequestProtocolOp request,
291                                            final List<Control> controls)
292      {
293        final StringBuilder b = getBuffer();
294        appendHeader(b, messageID);
295    
296        b.append("     Bind Request Protocol Op:").append(EOL);
297        b.append("          LDAP Version:  ").append(request.getVersion()).
298             append(EOL);
299        b.append("          Bind DN:  ").append(request.getBindDN()).append(EOL);
300    
301        switch (request.getCredentialsType())
302        {
303          case BindRequestProtocolOp.CRED_TYPE_SIMPLE:
304            b.append("          Credentials Type:  SIMPLE").append(EOL);
305            b.append("               Password:  ").
306                 append(request.getSimplePassword()).append(EOL);
307            break;
308    
309          case BindRequestProtocolOp.CRED_TYPE_SASL:
310            b.append("          Credentials Type:  SASL").append(EOL);
311            b.append("               Mechanism:  ").
312                 append(request.getSASLMechanism()).append(EOL);
313    
314            final ASN1OctetString saslCredentials = request.getSASLCredentials();
315            if (saslCredentials != null)
316            {
317              b.append("               Encoded Credentials:");
318              b.append(EOL);
319              toHexPlusASCII(saslCredentials.getValue(), 20, b);
320            }
321            break;
322        }
323    
324        appendControls(b, controls);
325        logHandler.publish(new LogRecord(Level.INFO, b.toString()));
326    
327        final LDAPMessage responseMessage = requestHandler.processBindRequest(
328             messageID, request, controls);
329    
330        b.setLength(0);
331        appendHeader(b, responseMessage.getMessageID());
332        b.append("     Bind Response Protocol Op:").append(EOL);
333    
334        final BindResponseProtocolOp protocolOp =
335             responseMessage.getBindResponseProtocolOp();
336        appendResponse(b, protocolOp.getResultCode(),
337             protocolOp.getDiagnosticMessage(),
338             protocolOp.getMatchedDN(), protocolOp.getReferralURLs());
339    
340        final ASN1OctetString serverSASLCredentials =
341             protocolOp.getServerSASLCredentials();
342        if (serverSASLCredentials != null)
343        {
344          b.append("               Encoded Server SASL Credentials:");
345          b.append(EOL);
346          toHexPlusASCII(serverSASLCredentials.getValue(), 20, b);
347        }
348    
349        appendControls(b, responseMessage.getControls());
350        logHandler.publish(new LogRecord(Level.INFO, b.toString()));
351    
352        return responseMessage;
353      }
354    
355    
356    
357      /**
358       * {@inheritDoc}
359       */
360      @Override()
361      public LDAPMessage processCompareRequest(final int messageID,
362                              final CompareRequestProtocolOp request,
363                              final List<Control> controls)
364      {
365        final StringBuilder b = getBuffer();
366        appendHeader(b, messageID);
367    
368        b.append("     Compare Request Protocol Op:").append(EOL);
369        b.append("          DN:  ").append(request.getDN()).append(EOL);
370        b.append("          Attribute Type:  ").append(request.getAttributeName()).
371             append(EOL);
372        b.append("          Assertion Value:  ").
373             append(request.getAssertionValue().stringValue()).append(EOL);
374    
375        appendControls(b, controls);
376        logHandler.publish(new LogRecord(Level.INFO, b.toString()));
377    
378        final LDAPMessage responseMessage = requestHandler.processCompareRequest(
379             messageID, request, controls);
380    
381        b.setLength(0);
382        appendHeader(b, responseMessage.getMessageID());
383        b.append("     Compare Response Protocol Op:").append(EOL);
384    
385        final CompareResponseProtocolOp protocolOp =
386             responseMessage.getCompareResponseProtocolOp();
387        appendResponse(b, protocolOp.getResultCode(),
388             protocolOp.getDiagnosticMessage(),
389             protocolOp.getMatchedDN(), protocolOp.getReferralURLs());
390    
391        appendControls(b, responseMessage.getControls());
392        logHandler.publish(new LogRecord(Level.INFO, b.toString()));
393    
394        return responseMessage;
395      }
396    
397    
398    
399      /**
400       * {@inheritDoc}
401       */
402      @Override()
403      public LDAPMessage processDeleteRequest(final int messageID,
404                                              final DeleteRequestProtocolOp request,
405                                              final List<Control> controls)
406      {
407        final StringBuilder b = getBuffer();
408        appendHeader(b, messageID);
409    
410        b.append("     Delete Request Protocol Op:").append(EOL);
411        b.append("          DN:  ").append(request.getDN()).append(EOL);
412    
413        appendControls(b, controls);
414        logHandler.publish(new LogRecord(Level.INFO, b.toString()));
415    
416        final LDAPMessage responseMessage = requestHandler.processDeleteRequest(
417             messageID, request, controls);
418    
419        b.setLength(0);
420        appendHeader(b, responseMessage.getMessageID());
421        b.append("     Delete Response Protocol Op:").append(EOL);
422    
423        final DeleteResponseProtocolOp protocolOp =
424             responseMessage.getDeleteResponseProtocolOp();
425        appendResponse(b, protocolOp.getResultCode(),
426             protocolOp.getDiagnosticMessage(),
427             protocolOp.getMatchedDN(), protocolOp.getReferralURLs());
428    
429        appendControls(b, responseMessage.getControls());
430        logHandler.publish(new LogRecord(Level.INFO, b.toString()));
431    
432        return responseMessage;
433      }
434    
435    
436    
437      /**
438       * {@inheritDoc}
439       */
440      @Override()
441      public LDAPMessage processExtendedRequest(final int messageID,
442                              final ExtendedRequestProtocolOp request,
443                              final List<Control> controls)
444      {
445        final StringBuilder b = getBuffer();
446        appendHeader(b, messageID);
447    
448        b.append("     Extended Request Protocol Op:").append(EOL);
449        b.append("          Request OID:  ").append(request.getOID()).append(EOL);
450    
451        final ASN1OctetString requestValue = request.getValue();
452        if (requestValue != null)
453        {
454          b.append("          Encoded Request Value:");
455          b.append(EOL);
456          toHexPlusASCII(requestValue.getValue(), 15, b);
457        }
458    
459        appendControls(b, controls);
460        logHandler.publish(new LogRecord(Level.INFO, b.toString()));
461    
462        final LDAPMessage responseMessage = requestHandler.processExtendedRequest(
463             messageID, request, controls);
464    
465        b.setLength(0);
466        appendHeader(b, responseMessage.getMessageID());
467        b.append("     Extended Response Protocol Op:").append(EOL);
468    
469        final ExtendedResponseProtocolOp protocolOp =
470             responseMessage.getExtendedResponseProtocolOp();
471        appendResponse(b, protocolOp.getResultCode(),
472             protocolOp.getDiagnosticMessage(),
473             protocolOp.getMatchedDN(), protocolOp.getReferralURLs());
474    
475        final String responseOID = protocolOp.getResponseOID();
476        if (responseOID != null)
477        {
478          b.append("          Response OID:  ").append(responseOID).append(EOL);
479        }
480    
481        final ASN1OctetString responseValue = protocolOp.getResponseValue();
482        if (responseValue != null)
483        {
484          b.append("          Encoded Response Value:");
485          b.append(EOL);
486          toHexPlusASCII(responseValue.getValue(), 15, b);
487        }
488    
489        appendControls(b, responseMessage.getControls());
490        logHandler.publish(new LogRecord(Level.INFO, b.toString()));
491    
492        return responseMessage;
493      }
494    
495    
496    
497      /**
498       * {@inheritDoc}
499       */
500      @Override()
501      public LDAPMessage processModifyRequest(final int messageID,
502                                              final ModifyRequestProtocolOp request,
503                                              final List<Control> controls)
504      {
505        final StringBuilder b = getBuffer();
506        appendHeader(b, messageID);
507    
508        b.append("     Modify Request Protocol Op:").append(EOL);
509    
510        final LDIFModifyChangeRecord changeRecord =
511             new LDIFModifyChangeRecord(request.getDN(),
512                  request.getModifications());
513        final String[] ldifLines = changeRecord.toLDIF(80);
514        for (final String line : ldifLines)
515        {
516          b.append("          ").append(line).append(EOL);
517        }
518    
519        appendControls(b, controls);
520        logHandler.publish(new LogRecord(Level.INFO, b.toString()));
521    
522        final LDAPMessage responseMessage = requestHandler.processModifyRequest(
523             messageID, request, controls);
524    
525        b.setLength(0);
526        appendHeader(b, responseMessage.getMessageID());
527        b.append("     Modify Response Protocol Op:").append(EOL);
528    
529        final ModifyResponseProtocolOp protocolOp =
530             responseMessage.getModifyResponseProtocolOp();
531        appendResponse(b, protocolOp.getResultCode(),
532             protocolOp.getDiagnosticMessage(),
533             protocolOp.getMatchedDN(), protocolOp.getReferralURLs());
534    
535        appendControls(b, responseMessage.getControls());
536        logHandler.publish(new LogRecord(Level.INFO, b.toString()));
537    
538        return responseMessage;
539      }
540    
541    
542    
543      /**
544       * {@inheritDoc}
545       */
546      @Override()
547      public LDAPMessage processModifyDNRequest(final int messageID,
548                              final ModifyDNRequestProtocolOp request,
549                              final List<Control> controls)
550      {
551        final StringBuilder b = getBuffer();
552        appendHeader(b, messageID);
553    
554        b.append("     Modify DN Request Protocol Op:").append(EOL);
555        b.append("          DN:  ").append(request.getDN()).append(EOL);
556        b.append("          New RDN:  ").append(request.getNewRDN()).append(EOL);
557        b.append("          Delete Old RDN:  ").append(request.deleteOldRDN()).
558             append(EOL);
559    
560        final String newSuperior = request.getNewSuperiorDN();
561        if (newSuperior != null)
562        {
563          b.append("          New Superior DN:  ").append(newSuperior).append(EOL);
564        }
565    
566        appendControls(b, controls);
567        logHandler.publish(new LogRecord(Level.INFO, b.toString()));
568    
569        final LDAPMessage responseMessage = requestHandler.processModifyDNRequest(
570             messageID, request, controls);
571    
572        b.setLength(0);
573        appendHeader(b, responseMessage.getMessageID());
574        b.append("     Modify DN Response Protocol Op:").append(EOL);
575    
576        final ModifyDNResponseProtocolOp protocolOp =
577             responseMessage.getModifyDNResponseProtocolOp();
578        appendResponse(b, protocolOp.getResultCode(),
579             protocolOp.getDiagnosticMessage(),
580             protocolOp.getMatchedDN(), protocolOp.getReferralURLs());
581    
582        appendControls(b, responseMessage.getControls());
583        logHandler.publish(new LogRecord(Level.INFO, b.toString()));
584    
585        return responseMessage;
586      }
587    
588    
589    
590      /**
591       * {@inheritDoc}
592       */
593      @Override()
594      public LDAPMessage processSearchRequest(final int messageID,
595                                              final SearchRequestProtocolOp request,
596                                              final List<Control> controls)
597      {
598        final StringBuilder b = getBuffer();
599        appendHeader(b, messageID);
600    
601        b.append("     Search Request Protocol Op:").append(EOL);
602        b.append("          Base DN:  ").append(request.getBaseDN()).append(EOL);
603        b.append("          Scope:  ").append(request.getScope()).append(EOL);
604        b.append("          Dereference Policy:  ").
605             append(request.getDerefPolicy()).append(EOL);
606        b.append("          Size Limit:  ").append(request.getSizeLimit()).
607             append(EOL);
608        b.append("          Time Limit:  ").append(request.getSizeLimit()).
609             append(EOL);
610        b.append("          Types Only:  ").append(request.typesOnly()).append(EOL);
611        b.append("          Filter:  ");
612        request.getFilter().toString(b);
613        b.append(EOL);
614    
615        final List<String> attributes = request.getAttributes();
616        if (! attributes.isEmpty())
617        {
618          b.append("          Requested Attributes:").append(EOL);
619          for (final String attr : attributes)
620          {
621            b.append("               ").append(attr).append(EOL);
622          }
623        }
624    
625        appendControls(b, controls);
626        logHandler.publish(new LogRecord(Level.INFO, b.toString()));
627    
628        final LDAPMessage responseMessage = requestHandler.processSearchRequest(
629             messageID, request, controls);
630    
631        b.setLength(0);
632        appendHeader(b, responseMessage.getMessageID());
633        b.append("     Search Result Done Protocol Op:").append(EOL);
634    
635        final SearchResultDoneProtocolOp protocolOp =
636             responseMessage.getSearchResultDoneProtocolOp();
637        appendResponse(b, protocolOp.getResultCode(),
638             protocolOp.getDiagnosticMessage(),
639             protocolOp.getMatchedDN(), protocolOp.getReferralURLs());
640    
641        appendControls(b, responseMessage.getControls());
642        logHandler.publish(new LogRecord(Level.INFO, b.toString()));
643    
644        return responseMessage;
645      }
646    
647    
648    
649      /**
650       * {@inheritDoc}
651       */
652      @Override()
653      public void processUnbindRequest(final int messageID,
654                                       final UnbindRequestProtocolOp request,
655                                       final List<Control> controls)
656      {
657        final StringBuilder b = getBuffer();
658        appendHeader(b, messageID);
659    
660        b.append("     Unbind Request Protocol Op:").append(EOL);
661    
662        appendControls(b, controls);
663        logHandler.publish(new LogRecord(Level.INFO, b.toString()));
664    
665        requestHandler.processUnbindRequest(messageID, request, controls);
666      }
667    
668    
669    
670      /**
671       * Retrieves a {@code StringBuilder} that may be used to generate a log
672       * message.
673       *
674       * @return  A {@code StringBuilder} containing the LDAP message header.
675       */
676      private static StringBuilder getBuffer()
677      {
678        StringBuilder b = BUFFERS.get();
679        if (b == null)
680        {
681          b = new StringBuilder();
682          BUFFERS.set(b);
683        }
684        else
685        {
686          b.setLength(0);
687        }
688    
689        return b;
690      }
691    
692    
693    
694      /**
695       * Appends an LDAP message header to the provided buffer.
696       *
697       * @param  b          The buffer to which to write the header.
698       * @param  messageID  The message ID for the LDAP message.
699       */
700      private void appendHeader(final StringBuilder b, final int messageID)
701      {
702        b.append(headerString);
703        b.append("LDAP Message:").append(EOL);
704        b.append("     Message ID:  ").append(messageID).append(EOL);
705      }
706    
707    
708    
709      /**
710       * Appends information about an LDAP response to the given buffer.
711       *
712       * @param  b                  The buffer to which to append the information.
713       * @param  resultCode         The result code for the response.
714       * @param  diagnosticMessage  The diagnostic message for the response, if any.
715       * @param  matchedDN          The matched DN for the response, if any.
716       * @param  referralURLs       The referral URLs for the response, if any.
717       */
718      private static void appendResponse(final StringBuilder b,
719                                         final int resultCode,
720                                         final String diagnosticMessage,
721                                         final String matchedDN,
722                                         final List<String> referralURLs)
723      {
724        b.append("          Result Code:  ").append(ResultCode.valueOf(resultCode)).
725             append(EOL);
726    
727        if (diagnosticMessage != null)
728        {
729          b.append("          Diagnostic Message:  ").append(diagnosticMessage).
730               append(EOL);
731        }
732    
733        if (matchedDN != null)
734        {
735          b.append("          Matched DN:  ").append(matchedDN).append(EOL);
736        }
737    
738        if (! referralURLs.isEmpty())
739        {
740          b.append("          Referral URLs:").append(EOL);
741          for (final String url : referralURLs)
742          {
743            b.append("               ").append(url).append(EOL);
744          }
745        }
746      }
747    
748    
749    
750      /**
751       * Appends information about the provided set of controls to the given buffer.
752       * A trailing EOL will also be appended.
753       *
754       * @param  b         The buffer to which to append the control information.
755       * @param  controls  The set of controls to be appended to the buffer.
756       */
757      private static void appendControls(final StringBuilder b,
758                                         final List<Control> controls)
759      {
760        if (! controls.isEmpty())
761        {
762          b.append("     Controls:").append(EOL);
763    
764          int index = 1;
765          for (final Control c : controls)
766          {
767            b.append("          Control ");
768            b.append(index++);
769            b.append(EOL);
770            b.append("               OID:  ");
771            b.append(c.getOID());
772            b.append(EOL);
773            b.append("               Is Critical:  ");
774            b.append(c.isCritical());
775            b.append(EOL);
776    
777            final ASN1OctetString value = c.getValue();
778            if ((value != null) && (value.getValueLength() > 0))
779            {
780              b.append("               Encoded Value:");
781              b.append(EOL);
782              toHexPlusASCII(value.getValue(), 20, b);
783            }
784    
785            // If it is a subclass of Control rather than just a generic one, then
786            // it might have a useful toString representation, so provide it.
787            if (! c.getClass().getName().equals(Control.class.getName()))
788            {
789              b.append("               String Representation:  ");
790              c.toString(b);
791              b.append(EOL);
792            }
793          }
794        }
795      }
796    
797    
798    
799      /**
800       * Appends information about the provided set of controls to the given buffer.
801       *
802       * @param  b         The buffer to which to append the control information.
803       * @param  controls  The set of controls to be appended to the buffer.
804       */
805      private static void appendControls(final StringBuilder b,
806                                         final Control[] controls)
807      {
808        appendControls(b, Arrays.asList(controls));
809      }
810    
811    
812    
813      /**
814       * {@inheritDoc}
815       */
816      public ObjectPair<IntermediateResponseProtocolOp,Control[]>
817                  transformIntermediateResponse(final int messageID,
818                       final IntermediateResponseProtocolOp response,
819                       final Control[] controls)
820      {
821        final StringBuilder b = getBuffer();
822        appendHeader(b, messageID);
823    
824        b.append("     Intermediate Response Protocol Op:").append(EOL);
825    
826        final String oid = response.getOID();
827        if (oid != null)
828        {
829          b.append("          OID:  ").append(oid).append(EOL);
830        }
831    
832        final ASN1OctetString value = response.getValue();
833        if (value != null)
834        {
835          b.append("          Encoded Value:");
836          b.append(EOL);
837          toHexPlusASCII(value.getValue(), 15, b);
838        }
839    
840        appendControls(b, controls);
841        logHandler.publish(new LogRecord(Level.INFO, b.toString()));
842    
843        return new ObjectPair<IntermediateResponseProtocolOp,Control[]>(response,
844             controls);
845      }
846    
847    
848    
849      /**
850       * {@inheritDoc}
851       */
852      public ObjectPair<SearchResultEntryProtocolOp,Control[]> transformEntry(
853                  final int messageID, final SearchResultEntryProtocolOp entry,
854                  final Control[] controls)
855      {
856        final StringBuilder b = getBuffer();
857        appendHeader(b, messageID);
858    
859        b.append("     Search Result Entry Protocol Op:").append(EOL);
860    
861        final Entry e = new Entry(entry.getDN(), entry.getAttributes());
862        final String[] ldifLines = e.toLDIF(80);
863        for (final String line : ldifLines)
864        {
865          b.append("          ").append(line).append(EOL);
866        }
867    
868        appendControls(b, controls);
869        logHandler.publish(new LogRecord(Level.INFO, b.toString()));
870    
871        return new ObjectPair<SearchResultEntryProtocolOp,Control[]>(entry,
872             controls);
873      }
874    
875    
876    
877      /**
878       * {@inheritDoc}
879       */
880      public ObjectPair<SearchResultReferenceProtocolOp,Control[]>
881                  transformReference(final int messageID,
882                       final SearchResultReferenceProtocolOp reference,
883                       final Control[] controls)
884      {
885        final StringBuilder b = getBuffer();
886        appendHeader(b, messageID);
887    
888        b.append("     Search Result Reference Protocol Op:").append(EOL);
889        b.append("          Referral URLs:").append(EOL);
890    
891        for (final String url : reference.getReferralURLs())
892        {
893          b.append("               ").append(url).append(EOL);
894        }
895    
896        appendControls(b, controls);
897        logHandler.publish(new LogRecord(Level.INFO, b.toString()));
898    
899        return new ObjectPair<SearchResultReferenceProtocolOp,Control[]>(reference,
900             controls);
901      }
902    }