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}