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