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.text.DecimalFormat; 042import java.text.SimpleDateFormat; 043import java.util.Date; 044import java.util.Iterator; 045import java.util.List; 046import java.util.concurrent.ConcurrentHashMap; 047import java.util.concurrent.atomic.AtomicLong; 048import java.util.logging.Handler; 049import java.util.logging.Level; 050import java.util.logging.LogRecord; 051 052import com.unboundid.ldap.protocol.AbandonRequestProtocolOp; 053import com.unboundid.ldap.protocol.AddRequestProtocolOp; 054import com.unboundid.ldap.protocol.AddResponseProtocolOp; 055import com.unboundid.ldap.protocol.BindRequestProtocolOp; 056import com.unboundid.ldap.protocol.BindResponseProtocolOp; 057import com.unboundid.ldap.protocol.CompareRequestProtocolOp; 058import com.unboundid.ldap.protocol.CompareResponseProtocolOp; 059import com.unboundid.ldap.protocol.DeleteRequestProtocolOp; 060import com.unboundid.ldap.protocol.DeleteResponseProtocolOp; 061import com.unboundid.ldap.protocol.ExtendedRequestProtocolOp; 062import com.unboundid.ldap.protocol.ExtendedResponseProtocolOp; 063import com.unboundid.ldap.protocol.LDAPMessage; 064import com.unboundid.ldap.protocol.ModifyRequestProtocolOp; 065import com.unboundid.ldap.protocol.ModifyResponseProtocolOp; 066import com.unboundid.ldap.protocol.ModifyDNRequestProtocolOp; 067import com.unboundid.ldap.protocol.ModifyDNResponseProtocolOp; 068import com.unboundid.ldap.protocol.SearchRequestProtocolOp; 069import com.unboundid.ldap.protocol.SearchResultDoneProtocolOp; 070import com.unboundid.ldap.protocol.SearchResultEntryProtocolOp; 071import com.unboundid.ldap.protocol.UnbindRequestProtocolOp; 072import com.unboundid.ldap.sdk.Control; 073import com.unboundid.ldap.sdk.LDAPException; 074import com.unboundid.util.NotMutable; 075import com.unboundid.util.ObjectPair; 076import com.unboundid.util.StaticUtils; 077import com.unboundid.util.ThreadSafety; 078import com.unboundid.util.ThreadSafetyLevel; 079import com.unboundid.util.Validator; 080 081 082 083/** 084 * This class provides a request handler that may be used to log each request 085 * and result using the Java logging framework. It will be also be associated 086 * with another request handler that will actually be used to handle the 087 * request. 088 */ 089@NotMutable() 090@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 091public final class AccessLogRequestHandler 092 extends LDAPListenerRequestHandler 093 implements SearchEntryTransformer 094{ 095 // The operation ID counter that will be used for this request handler 096 // instance. 097 private final AtomicLong nextOperationID; 098 099 // A map used to correlate the number of search result entries returned for a 100 // particular message ID. 101 private final ConcurrentHashMap<Integer,AtomicLong> entryCounts = 102 new ConcurrentHashMap<>(StaticUtils.computeMapCapacity(50)); 103 104 // The log handler that will be used to log the messages. 105 private final Handler logHandler; 106 107 // The client connection with which this request handler is associated. 108 private final LDAPListenerClientConnection clientConnection; 109 110 // The request handler that actually will be used to process any requests 111 // received. 112 private final LDAPListenerRequestHandler requestHandler; 113 114 // The thread-local decimal formatters that will be used to format etime 115 // values. 116 private final ThreadLocal<DecimalFormat> decimalFormatters; 117 118 // The thread-local date formatters that will be used to format timestamps. 119 private final ThreadLocal<SimpleDateFormat> timestampFormatters; 120 121 // The thread-local string builders that will be used to build log messages. 122 private final ThreadLocal<StringBuilder> buffers; 123 124 125 126 /** 127 * Creates a new access log request handler that will log request and result 128 * messages using the provided log handler, and will process client requests 129 * using the provided request handler. 130 * 131 * @param logHandler The log handler that will be used to log request 132 * and result messages. Note that all messages will 133 * be logged at the INFO level. It must not be 134 * {@code null}. Note that the log handler will not 135 * be automatically closed when the associated 136 * listener is shut down. 137 * @param requestHandler The request handler that will actually be used to 138 * process any requests received. It must not be 139 * {@code null}. 140 */ 141 public AccessLogRequestHandler(final Handler logHandler, 142 final LDAPListenerRequestHandler requestHandler) 143 { 144 Validator.ensureNotNull(logHandler, requestHandler); 145 146 this.logHandler = logHandler; 147 this.requestHandler = requestHandler; 148 149 decimalFormatters = new ThreadLocal<>(); 150 timestampFormatters = new ThreadLocal<>(); 151 buffers = new ThreadLocal<>(); 152 153 nextOperationID = null; 154 clientConnection = null; 155 } 156 157 158 159 /** 160 * Creates a new access log request handler that will log request and result 161 * messages using the provided log handler, and will process client requests 162 * using the provided request handler. 163 * 164 * @param logHandler The log handler that will be used to log request 165 * and result messages. Note that all messages will 166 * be logged at the INFO level. It must not be 167 * {@code null}. 168 * @param requestHandler The request handler that will actually be used to 169 * process any requests received. It must not be 170 * {@code null}. 171 * @param clientConnection The client connection with which this instance is 172 * associated. 173 * @param buffers The thread-local string builders that will be 174 * used to build log messages. 175 * @param timestampFormatters The thread-local date formatters that will be 176 * used to format timestamps. 177 * @param decimalFormatters The thread-local decimal formatters that 178 * will be used to format etime values. 179 */ 180 private AccessLogRequestHandler(final Handler logHandler, 181 final LDAPListenerRequestHandler requestHandler, 182 final LDAPListenerClientConnection clientConnection, 183 final ThreadLocal<StringBuilder> buffers, 184 final ThreadLocal<SimpleDateFormat> timestampFormatters, 185 final ThreadLocal<DecimalFormat> decimalFormatters) 186 { 187 this.logHandler = logHandler; 188 this.requestHandler = requestHandler; 189 this.clientConnection = clientConnection; 190 this.buffers = buffers; 191 this.timestampFormatters = timestampFormatters; 192 this.decimalFormatters = decimalFormatters; 193 194 nextOperationID = new AtomicLong(0L); 195 } 196 197 198 199 /** 200 * {@inheritDoc} 201 */ 202 @Override() 203 public AccessLogRequestHandler newInstance( 204 final LDAPListenerClientConnection connection) 205 throws LDAPException 206 { 207 final AccessLogRequestHandler h = new AccessLogRequestHandler(logHandler, 208 requestHandler.newInstance(connection), connection, buffers, 209 timestampFormatters, decimalFormatters); 210 connection.addSearchEntryTransformer(h); 211 212 final StringBuilder b = h.getConnectionHeader("CONNECT"); 213 214 final Socket s = connection.getSocket(); 215 b.append(" from=\""); 216 b.append(s.getInetAddress().getHostAddress()); 217 b.append(':'); 218 b.append(s.getPort()); 219 b.append("\" to=\""); 220 b.append(s.getLocalAddress().getHostAddress()); 221 b.append(':'); 222 b.append(s.getLocalPort()); 223 b.append('"'); 224 225 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 226 logHandler.flush(); 227 228 return h; 229 } 230 231 232 233 /** 234 * {@inheritDoc} 235 */ 236 @Override() 237 public void closeInstance() 238 { 239 final StringBuilder b = getConnectionHeader("DISCONNECT"); 240 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 241 logHandler.flush(); 242 243 requestHandler.closeInstance(); 244 } 245 246 247 248 /** 249 * {@inheritDoc} 250 */ 251 @Override() 252 public void processAbandonRequest(final int messageID, 253 final AbandonRequestProtocolOp request, 254 final List<Control> controls) 255 { 256 final StringBuilder b = getRequestHeader("ABANDON", 257 nextOperationID.getAndIncrement(), messageID); 258 259 b.append(" idToAbandon="); 260 b.append(request.getIDToAbandon()); 261 262 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 263 logHandler.flush(); 264 265 requestHandler.processAbandonRequest(messageID, request, controls); 266 } 267 268 269 270 /** 271 * {@inheritDoc} 272 */ 273 @Override() 274 public LDAPMessage processAddRequest(final int messageID, 275 final AddRequestProtocolOp request, 276 final List<Control> controls) 277 { 278 final long opID = nextOperationID.getAndIncrement(); 279 280 final StringBuilder b = getRequestHeader("ADD", opID, messageID); 281 282 b.append(" dn=\""); 283 b.append(request.getDN()); 284 b.append('"'); 285 286 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 287 logHandler.flush(); 288 289 final long startTimeNanos = System.nanoTime(); 290 final LDAPMessage responseMessage = requestHandler.processAddRequest( 291 messageID, request, controls); 292 final long eTimeNanos = System.nanoTime() - startTimeNanos; 293 final AddResponseProtocolOp protocolOp = 294 responseMessage.getAddResponseProtocolOp(); 295 296 generateResponse(b, "ADD", opID, messageID, protocolOp.getResultCode(), 297 protocolOp.getDiagnosticMessage(), protocolOp.getMatchedDN(), 298 protocolOp.getReferralURLs(), eTimeNanos); 299 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 public LDAPMessage processBindRequest(final int messageID, 313 final BindRequestProtocolOp request, 314 final List<Control> controls) 315 { 316 final long opID = nextOperationID.getAndIncrement(); 317 318 final StringBuilder b = getRequestHeader("BIND", opID, messageID); 319 320 b.append(" version="); 321 b.append(request.getVersion()); 322 b.append(" dn=\""); 323 b.append(request.getBindDN()); 324 b.append("\" authType=\""); 325 326 switch (request.getCredentialsType()) 327 { 328 case BindRequestProtocolOp.CRED_TYPE_SIMPLE: 329 b.append("SIMPLE"); 330 break; 331 332 case BindRequestProtocolOp.CRED_TYPE_SASL: 333 b.append("SASL "); 334 b.append(request.getSASLMechanism()); 335 break; 336 } 337 338 b.append('"'); 339 340 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 341 logHandler.flush(); 342 343 final long startTimeNanos = System.nanoTime(); 344 final LDAPMessage responseMessage = requestHandler.processBindRequest( 345 messageID, request, controls); 346 final long eTimeNanos = System.nanoTime() - startTimeNanos; 347 final BindResponseProtocolOp protocolOp = 348 responseMessage.getBindResponseProtocolOp(); 349 350 generateResponse(b, "BIND", opID, messageID, protocolOp.getResultCode(), 351 protocolOp.getDiagnosticMessage(), protocolOp.getMatchedDN(), 352 protocolOp.getReferralURLs(), eTimeNanos); 353 354 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 355 logHandler.flush(); 356 357 return responseMessage; 358 } 359 360 361 362 /** 363 * {@inheritDoc} 364 */ 365 @Override() 366 public LDAPMessage processCompareRequest(final int messageID, 367 final CompareRequestProtocolOp request, 368 final List<Control> controls) 369 { 370 final long opID = nextOperationID.getAndIncrement(); 371 372 final StringBuilder b = getRequestHeader("COMPARE", opID, messageID); 373 374 b.append(" dn=\""); 375 b.append(request.getDN()); 376 b.append("\" attr=\""); 377 b.append(request.getAttributeName()); 378 b.append('"'); 379 380 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 381 logHandler.flush(); 382 383 final long startTimeNanos = System.nanoTime(); 384 final LDAPMessage responseMessage = requestHandler.processCompareRequest( 385 messageID, request, controls); 386 final long eTimeNanos = System.nanoTime() - startTimeNanos; 387 final CompareResponseProtocolOp protocolOp = 388 responseMessage.getCompareResponseProtocolOp(); 389 390 generateResponse(b, "COMPARE", opID, messageID, protocolOp.getResultCode(), 391 protocolOp.getDiagnosticMessage(), protocolOp.getMatchedDN(), 392 protocolOp.getReferralURLs(), eTimeNanos); 393 394 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 395 logHandler.flush(); 396 397 return responseMessage; 398 } 399 400 401 402 /** 403 * {@inheritDoc} 404 */ 405 @Override() 406 public LDAPMessage processDeleteRequest(final int messageID, 407 final DeleteRequestProtocolOp request, 408 final List<Control> controls) 409 { 410 final long opID = nextOperationID.getAndIncrement(); 411 412 final StringBuilder b = getRequestHeader("DELETE", opID, messageID); 413 414 b.append(" dn=\""); 415 b.append(request.getDN()); 416 b.append('"'); 417 418 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 419 logHandler.flush(); 420 421 final long startTimeNanos = System.nanoTime(); 422 final LDAPMessage responseMessage = requestHandler.processDeleteRequest( 423 messageID, request, controls); 424 final long eTimeNanos = System.nanoTime() - startTimeNanos; 425 final DeleteResponseProtocolOp protocolOp = 426 responseMessage.getDeleteResponseProtocolOp(); 427 428 generateResponse(b, "DELETE", opID, messageID, protocolOp.getResultCode(), 429 protocolOp.getDiagnosticMessage(), protocolOp.getMatchedDN(), 430 protocolOp.getReferralURLs(), eTimeNanos); 431 432 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 433 logHandler.flush(); 434 435 return responseMessage; 436 } 437 438 439 440 /** 441 * {@inheritDoc} 442 */ 443 @Override() 444 public LDAPMessage processExtendedRequest(final int messageID, 445 final ExtendedRequestProtocolOp request, 446 final List<Control> controls) 447 { 448 final long opID = nextOperationID.getAndIncrement(); 449 450 final StringBuilder b = getRequestHeader("EXTENDED", opID, messageID); 451 452 b.append(" requestOID=\""); 453 b.append(request.getOID()); 454 b.append('"'); 455 456 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 457 logHandler.flush(); 458 459 final long startTimeNanos = System.nanoTime(); 460 final LDAPMessage responseMessage = requestHandler.processExtendedRequest( 461 messageID, request, controls); 462 final long eTimeNanos = System.nanoTime() - startTimeNanos; 463 final ExtendedResponseProtocolOp protocolOp = 464 responseMessage.getExtendedResponseProtocolOp(); 465 466 generateResponse(b, "EXTENDED", opID, messageID, protocolOp.getResultCode(), 467 protocolOp.getDiagnosticMessage(), protocolOp.getMatchedDN(), 468 protocolOp.getReferralURLs(), eTimeNanos); 469 470 final String responseOID = protocolOp.getResponseOID(); 471 if (responseOID != null) 472 { 473 b.append(" responseOID=\""); 474 b.append(responseOID); 475 b.append('"'); 476 } 477 478 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 479 logHandler.flush(); 480 481 return responseMessage; 482 } 483 484 485 486 /** 487 * {@inheritDoc} 488 */ 489 @Override() 490 public LDAPMessage processModifyRequest(final int messageID, 491 final ModifyRequestProtocolOp request, 492 final List<Control> controls) 493 { 494 final long opID = nextOperationID.getAndIncrement(); 495 496 final StringBuilder b = getRequestHeader("MODIFY", opID, messageID); 497 498 b.append(" dn=\""); 499 b.append(request.getDN()); 500 b.append('"'); 501 502 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 503 logHandler.flush(); 504 505 final long startTimeNanos = System.nanoTime(); 506 final LDAPMessage responseMessage = requestHandler.processModifyRequest( 507 messageID, request, controls); 508 final long eTimeNanos = System.nanoTime() - startTimeNanos; 509 final ModifyResponseProtocolOp protocolOp = 510 responseMessage.getModifyResponseProtocolOp(); 511 512 generateResponse(b, "MODIFY", opID, messageID, protocolOp.getResultCode(), 513 protocolOp.getDiagnosticMessage(), protocolOp.getMatchedDN(), 514 protocolOp.getReferralURLs(), eTimeNanos); 515 516 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 517 logHandler.flush(); 518 519 return responseMessage; 520 } 521 522 523 524 /** 525 * {@inheritDoc} 526 */ 527 @Override() 528 public LDAPMessage processModifyDNRequest(final int messageID, 529 final ModifyDNRequestProtocolOp request, 530 final List<Control> controls) 531 { 532 final long opID = nextOperationID.getAndIncrement(); 533 534 final StringBuilder b = getRequestHeader("MODDN", opID, messageID); 535 536 b.append(" dn=\""); 537 b.append(request.getDN()); 538 b.append("\" newRDN=\""); 539 b.append(request.getNewRDN()); 540 b.append("\" deleteOldRDN="); 541 b.append(request.deleteOldRDN()); 542 543 final String newSuperior = request.getNewSuperiorDN(); 544 if (newSuperior != null) 545 { 546 b.append(" newSuperior=\""); 547 b.append(newSuperior); 548 b.append('"'); 549 } 550 551 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 552 logHandler.flush(); 553 554 final long startTimeNanos = System.nanoTime(); 555 final LDAPMessage responseMessage = requestHandler.processModifyDNRequest( 556 messageID, request, controls); 557 final long eTimeNanos = System.nanoTime() - startTimeNanos; 558 final ModifyDNResponseProtocolOp protocolOp = 559 responseMessage.getModifyDNResponseProtocolOp(); 560 561 generateResponse(b, "MODDN", opID, messageID, protocolOp.getResultCode(), 562 protocolOp.getDiagnosticMessage(), protocolOp.getMatchedDN(), 563 protocolOp.getReferralURLs(), eTimeNanos); 564 565 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 566 logHandler.flush(); 567 568 return responseMessage; 569 } 570 571 572 573 /** 574 * {@inheritDoc} 575 */ 576 @Override() 577 public LDAPMessage processSearchRequest(final int messageID, 578 final SearchRequestProtocolOp request, 579 final List<Control> controls) 580 { 581 final long opID = nextOperationID.getAndIncrement(); 582 583 final StringBuilder b = getRequestHeader("SEARCH", opID, messageID); 584 585 b.append(" base=\""); 586 b.append(request.getBaseDN()); 587 b.append("\" scope="); 588 b.append(request.getScope().intValue()); 589 b.append(" filter=\""); 590 request.getFilter().toString(b); 591 b.append("\" attrs=\""); 592 593 final List<String> attrList = request.getAttributes(); 594 if (attrList.isEmpty()) 595 { 596 b.append("ALL"); 597 } 598 else 599 { 600 final Iterator<String> iterator = attrList.iterator(); 601 while (iterator.hasNext()) 602 { 603 b.append(iterator.next()); 604 if (iterator.hasNext()) 605 { 606 b.append(','); 607 } 608 } 609 } 610 611 b.append('"'); 612 613 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 614 logHandler.flush(); 615 616 final AtomicLong l = new AtomicLong(0L); 617 entryCounts.put(messageID, l); 618 619 try 620 { 621 final long startTimeNanos = System.nanoTime(); 622 final LDAPMessage responseMessage = requestHandler.processSearchRequest( 623 messageID, request, controls); 624 final long eTimeNanos = System.nanoTime() - startTimeNanos; 625 final SearchResultDoneProtocolOp protocolOp = 626 responseMessage.getSearchResultDoneProtocolOp(); 627 628 generateResponse(b, "SEARCH", opID, messageID, protocolOp.getResultCode(), 629 protocolOp.getDiagnosticMessage(), protocolOp.getMatchedDN(), 630 protocolOp.getReferralURLs(), eTimeNanos); 631 632 b.append(" entriesReturned="); 633 b.append(l.get()); 634 635 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 636 logHandler.flush(); 637 638 return responseMessage; 639 } 640 finally 641 { 642 entryCounts.remove(messageID); 643 } 644 } 645 646 647 648 /** 649 * {@inheritDoc} 650 */ 651 @Override() 652 public void processUnbindRequest(final int messageID, 653 final UnbindRequestProtocolOp request, 654 final List<Control> controls) 655 { 656 final StringBuilder b = getRequestHeader("UNBIND", 657 nextOperationID.getAndIncrement(), messageID); 658 659 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 660 logHandler.flush(); 661 662 requestHandler.processUnbindRequest(messageID, request, controls); 663 } 664 665 666 667 /** 668 * Retrieves a string builder that can be used to construct a log message. 669 * 670 * @return A string builder that can be used to construct a log message. 671 */ 672 private StringBuilder getBuffer() 673 { 674 StringBuilder b = buffers.get(); 675 if (b == null) 676 { 677 b = new StringBuilder(); 678 buffers.set(b); 679 } 680 else 681 { 682 b.setLength(0); 683 } 684 685 return b; 686 } 687 688 689 690 /** 691 * Adds a timestamp to the beginning of the provided buffer. 692 * 693 * @param buffer The buffer to which the timestamp should be added. 694 */ 695 private void addTimestamp(final StringBuilder buffer) 696 { 697 SimpleDateFormat dateFormat = timestampFormatters.get(); 698 if (dateFormat == null) 699 { 700 dateFormat = new SimpleDateFormat("'['dd/MMM/yyyy:HH:mm:ss Z']'"); 701 timestampFormatters.set(dateFormat); 702 } 703 704 buffer.append(dateFormat.format(new Date())); 705 } 706 707 708 709 /** 710 * Retrieves a {@code StringBuilder} with header information for a request log 711 * message for the specified type of operation. 712 * 713 * @param messageType The type of operation being requested. 714 * 715 * @return A {@code StringBuilder} with header information appended for the 716 * request; 717 */ 718 private StringBuilder getConnectionHeader(final String messageType) 719 { 720 final StringBuilder b = getBuffer(); 721 addTimestamp(b); 722 b.append(' '); 723 b.append(messageType); 724 b.append(" conn="); 725 b.append(clientConnection.getConnectionID()); 726 727 return b; 728 } 729 730 731 732 /** 733 * Retrieves a {@code StringBuilder} with header information for a request log 734 * message for the specified type of operation. 735 * 736 * @param opType The type of operation being requested. 737 * @param opID The operation ID for the request. 738 * @param msgID The message ID for the request. 739 * 740 * @return A {@code StringBuilder} with header information appended for the 741 * request; 742 */ 743 private StringBuilder getRequestHeader(final String opType, final long opID, 744 final int msgID) 745 { 746 final StringBuilder b = getBuffer(); 747 addTimestamp(b); 748 b.append(' '); 749 b.append(opType); 750 b.append(" REQUEST conn="); 751 b.append(clientConnection.getConnectionID()); 752 b.append(" op="); 753 b.append(opID); 754 b.append(" msgID="); 755 b.append(msgID); 756 757 return b; 758 } 759 760 761 762 /** 763 * Writes information about the result of processing an operation to the 764 * given buffer. 765 * 766 * @param b The buffer to which the information should be 767 * written. The buffer will be cleared before 768 * adding any additional content. 769 * @param opType The type of operation that was processed. 770 * @param opID The operation ID for the response. 771 * @param msgID The message ID for the response. 772 * @param resultCode The result code for the response, if any. 773 * @param diagnosticMessage The diagnostic message for the response, if any. 774 * @param matchedDN The matched DN for the response, if any. 775 * @param referralURLs The referral URLs for the response, if any. 776 * @param eTimeNanos The length of time in nanoseconds required to 777 * process the operation. 778 */ 779 private void generateResponse(final StringBuilder b, final String opType, 780 final long opID, final int msgID, 781 final int resultCode, 782 final String diagnosticMessage, 783 final String matchedDN, 784 final List<String> referralURLs, 785 final long eTimeNanos) 786 { 787 b.setLength(0); 788 addTimestamp(b); 789 b.append(' '); 790 b.append(opType); 791 b.append(" RESULT conn="); 792 b.append(clientConnection.getConnectionID()); 793 b.append(" op="); 794 b.append(opID); 795 b.append(" msgID="); 796 b.append(msgID); 797 b.append(" resultCode="); 798 b.append(resultCode); 799 800 if (diagnosticMessage != null) 801 { 802 b.append(" diagnosticMessage=\""); 803 b.append(diagnosticMessage); 804 b.append('"'); 805 } 806 807 if (matchedDN != null) 808 { 809 b.append(" matchedDN=\""); 810 b.append(matchedDN); 811 b.append('"'); 812 } 813 814 if (! referralURLs.isEmpty()) 815 { 816 b.append(" referralURLs=\""); 817 final Iterator<String> iterator = referralURLs.iterator(); 818 while (iterator.hasNext()) 819 { 820 b.append(iterator.next()); 821 822 if (iterator.hasNext()) 823 { 824 b.append(','); 825 } 826 } 827 828 b.append('"'); 829 } 830 831 DecimalFormat f = decimalFormatters.get(); 832 if (f == null) 833 { 834 f = new DecimalFormat("0.000"); 835 decimalFormatters.set(f); 836 } 837 838 b.append(" etime="); 839 b.append(f.format(eTimeNanos / 1_000_000.0d)); 840 } 841 842 843 844 /** 845 * {@inheritDoc} 846 */ 847 @Override() 848 public ObjectPair<SearchResultEntryProtocolOp,Control[]> transformEntry( 849 final int messageID, final SearchResultEntryProtocolOp entry, 850 final Control[] controls) 851 { 852 final AtomicLong l = entryCounts.get(messageID); 853 if (l != null) 854 { 855 l.incrementAndGet(); 856 } 857 858 return new ObjectPair<>(entry, controls); 859 } 860}