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