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