001/* 002 * Copyright 2022-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2022-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) 2022-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.sdk.unboundidds.logs.v2.json; 037 038 039 040import java.io.File; 041import java.io.FileInputStream; 042import java.io.IOException; 043import java.io.InputStream; 044 045import com.unboundid.ldap.sdk.unboundidds.logs.AccessLogMessageType; 046import com.unboundid.ldap.sdk.unboundidds.logs.AccessLogOperationType; 047import com.unboundid.ldap.sdk.unboundidds.logs.LogException; 048import com.unboundid.ldap.sdk.unboundidds.logs.v2.AccessLogReader; 049import com.unboundid.util.Debug; 050import com.unboundid.util.NotNull; 051import com.unboundid.util.Nullable; 052import com.unboundid.util.StaticUtils; 053import com.unboundid.util.ThreadSafety; 054import com.unboundid.util.ThreadSafetyLevel; 055import com.unboundid.util.json.JSONException; 056import com.unboundid.util.json.JSONObject; 057import com.unboundid.util.json.JSONObjectReader; 058 059import static com.unboundid.ldap.sdk.unboundidds.logs.v2.json.JSONLogMessages.*; 060 061 062 063/** 064 * This class provides a mechanism for reading JSON-formatted access log 065 * messages. 066 * <BR> 067 * <BLOCKQUOTE> 068 * <B>NOTE:</B> This class, and other classes within the 069 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 070 * supported for use against Ping Identity, UnboundID, and 071 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 072 * for proprietary functionality or for external specifications that are not 073 * considered stable or mature enough to be guaranteed to work in an 074 * interoperable way with other types of LDAP servers. 075 * </BLOCKQUOTE> 076 */ 077@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 078public final class JSONAccessLogReader 079 implements AccessLogReader 080{ 081 // The JSON object reader that will be used to read log messages. 082 @NotNull private final JSONObjectReader jsonObjectReader; 083 084 085 086 /** 087 * Creates a new JSON access log reader that will read JSON-formatted access 088 * log messages from the specified file. 089 * 090 * @param logFilePath The path to the log file from which the access log 091 * messages will be read. It must not be {@code null}. 092 * 093 * @throws IOException If a problem occurs while opening the specified file 094 * for reading. 095 */ 096 public JSONAccessLogReader(@NotNull final String logFilePath) 097 throws IOException 098 { 099 this(new File(logFilePath)); 100 } 101 102 103 104 /** 105 * Creates a new JSON access log reader that will read JSON-formatted access 106 * log messages from the specified file. 107 * 108 * @param logFile The log file from which the access log messages will be 109 * read. It must not be {@code null}. 110 * 111 * @throws IOException If a problem occurs while opening the specified file 112 * for reading. 113 */ 114 public JSONAccessLogReader(@NotNull final File logFile) 115 throws IOException 116 { 117 this(new FileInputStream(logFile)); 118 } 119 120 121 122 /** 123 * Creates a new JSON access log reader that will read JSON-formatted access 124 * log messages from the provided input stream. 125 * 126 * @param inputStream The input stream from which the access log messages 127 * will be read. It must not be {@code null}. 128 */ 129 public JSONAccessLogReader(@NotNull final InputStream inputStream) 130 { 131 jsonObjectReader = new JSONObjectReader(inputStream); 132 } 133 134 135 136 /** 137 * {@inheritDoc} 138 */ 139 @Override() 140 @Nullable() 141 public JSONAccessLogMessage readMessage() 142 throws IOException, LogException 143 { 144 // Read the next JSON object from the log. If this fails, then throw an 145 // IOException to indicate that we can't continue reading. 146 final JSONObject messageObject; 147 try 148 { 149 messageObject = jsonObjectReader.readObject(); 150 } 151 catch (final JSONException e) 152 { 153 Debug.debugException(e); 154 throw new IOException( 155 ERR_JSON_ACCESS_LOG_READER_NOT_VALID_JSON.get( 156 StaticUtils.getExceptionMessage(e)), 157 e); 158 } 159 160 if (messageObject == null) 161 { 162 return null; 163 } 164 165 return parseMessage(messageObject); 166 } 167 168 169 170 /** 171 * Parses the contents of the provided JSON object as a JSON-formatted access 172 * log message. 173 * 174 * @param messageObject The JSON object to parse as an access log message. 175 * It must not be {@code null}. 176 * 177 * @return The parsed access log message. 178 * 179 * @throws LogException If the provided JSON object cannot be parsed as a 180 * valid access log message. 181 */ 182 @NotNull() 183 public static JSONAccessLogMessage parseMessage( 184 @NotNull final JSONObject messageObject) 185 throws LogException 186 { 187 // Determine the message type for the log message. 188 final String messageTypeStr = messageObject.getFieldAsString( 189 JSONFormattedAccessLogFields.MESSAGE_TYPE.getFieldName()); 190 if (messageTypeStr == null) 191 { 192 final String messageStr = messageObject.toSingleLineString(); 193 throw new LogException(messageStr, 194 ERR_JSON_ACCESS_LOG_READER_MISSING_MESSAGE_TYPE.get(messageStr, 195 JSONFormattedAccessLogFields.MESSAGE_TYPE.getFieldName())); 196 } 197 198 final AccessLogMessageType messageType = 199 AccessLogMessageType.forName(messageTypeStr); 200 if (messageType == null) 201 { 202 final String messageStr = messageObject.toSingleLineString(); 203 throw new LogException(messageStr, 204 ERR_JSON_ACCESS_LOG_READER_UNSUPPORTED_MESSAGE_TYPE.get(messageStr, 205 messageTypeStr)); 206 } 207 208 switch (messageType) 209 { 210 case CONNECT: 211 return new JSONConnectAccessLogMessage(messageObject); 212 case DISCONNECT: 213 return new JSONDisconnectAccessLogMessage(messageObject); 214 case SECURITY_NEGOTIATION: 215 return new JSONSecurityNegotiationAccessLogMessage(messageObject); 216 case CLIENT_CERTIFICATE: 217 return new JSONClientCertificateAccessLogMessage(messageObject); 218 case ENTRY_REBALANCING_REQUEST: 219 return new JSONEntryRebalancingRequestAccessLogMessage(messageObject); 220 case ENTRY_REBALANCING_RESULT: 221 return new JSONEntryRebalancingResultAccessLogMessage(messageObject); 222 case ENTRY: 223 return new JSONSearchEntryAccessLogMessage(messageObject); 224 case REFERENCE: 225 return new JSONSearchReferenceAccessLogMessage(messageObject); 226 case INTERMEDIATE_RESPONSE: 227 return new JSONIntermediateResponseAccessLogMessage(messageObject); 228 case REQUEST: 229 return createRequestMessage(messageObject); 230 case RESULT: 231 return createResultMessage(messageObject); 232 case FORWARD: 233 return createForwardMessage(messageObject); 234 case FORWARD_FAILED: 235 return createForwardFailedMessage(messageObject); 236 case ASSURANCE_COMPLETE: 237 return createAssuranceCompleteMessage(messageObject); 238 default: 239 final String messageStr = messageObject.toSingleLineString(); 240 throw new LogException(messageStr, 241 ERR_JSON_ACCESS_LOG_READER_UNSUPPORTED_MESSAGE_TYPE.get(messageStr, 242 messageTypeStr)); 243 } 244 } 245 246 247 248 /** 249 * Creates a new request access log message from the provided JSON object. 250 * 251 * @param messageObject The JSON object containing an encoded representation 252 * of a request access log message. It must not be 253 * {@code null}. 254 * 255 * @return The request access log message that was created. 256 * 257 * @throws LogException If the provided JSON object cannot be decoded as a 258 * valid request access log message. 259 */ 260 @NotNull() 261 private static JSONAccessLogMessage createRequestMessage( 262 @NotNull final JSONObject messageObject) 263 throws LogException 264 { 265 final AccessLogOperationType operationType = 266 getOperationType(messageObject); 267 switch (operationType) 268 { 269 case ABANDON: 270 return new JSONAbandonRequestAccessLogMessage(messageObject); 271 case ADD: 272 return new JSONAddRequestAccessLogMessage(messageObject); 273 case BIND: 274 return new JSONBindRequestAccessLogMessage(messageObject); 275 case COMPARE: 276 return new JSONCompareRequestAccessLogMessage(messageObject); 277 case DELETE: 278 return new JSONDeleteRequestAccessLogMessage(messageObject); 279 case EXTENDED: 280 return new JSONExtendedRequestAccessLogMessage(messageObject); 281 case MODIFY: 282 return new JSONModifyRequestAccessLogMessage(messageObject); 283 case MODDN: 284 return new JSONModifyDNRequestAccessLogMessage(messageObject); 285 case SEARCH: 286 return new JSONSearchRequestAccessLogMessage(messageObject); 287 case UNBIND: 288 return new JSONUnbindRequestAccessLogMessage(messageObject); 289 default: 290 final String messageStr = messageObject.toSingleLineString(); 291 throw new LogException(messageStr, 292 ERR_JSON_ACCESS_LOG_READER_UNSUPPORTED_REQUEST_OP_TYPE.get( 293 messageStr, operationType.getLogIdentifier())); 294 } 295 } 296 297 298 299 /** 300 * Creates a new result access log message from the provided JSON object. 301 * 302 * @param messageObject The JSON object containing an encoded representation 303 * of a result access log message. It must not be 304 * {@code null}. 305 * 306 * @return The result access log message that was created. 307 * 308 * @throws LogException If the provided JSON object cannot be decoded as a 309 * valid result access log message. 310 */ 311 @NotNull() 312 private static JSONAccessLogMessage createResultMessage( 313 @NotNull final JSONObject messageObject) 314 throws LogException 315 { 316 final AccessLogOperationType operationType = 317 getOperationType(messageObject); 318 switch (operationType) 319 { 320 case ABANDON: 321 return new JSONAbandonResultAccessLogMessage(messageObject); 322 case ADD: 323 return new JSONAddResultAccessLogMessage(messageObject); 324 case BIND: 325 return new JSONBindResultAccessLogMessage(messageObject); 326 case COMPARE: 327 return new JSONCompareResultAccessLogMessage(messageObject); 328 case DELETE: 329 return new JSONDeleteResultAccessLogMessage(messageObject); 330 case EXTENDED: 331 return new JSONExtendedResultAccessLogMessage(messageObject); 332 case MODIFY: 333 return new JSONModifyResultAccessLogMessage(messageObject); 334 case MODDN: 335 return new JSONModifyDNResultAccessLogMessage(messageObject); 336 case SEARCH: 337 return new JSONSearchResultAccessLogMessage(messageObject); 338 case UNBIND: 339 default: 340 final String messageStr = messageObject.toSingleLineString(); 341 throw new LogException(messageStr, 342 ERR_JSON_ACCESS_LOG_READER_UNSUPPORTED_RESULT_OP_TYPE.get( 343 messageStr, operationType.getLogIdentifier())); 344 } 345 } 346 347 348 349 /** 350 * Creates a new forward access log message from the provided JSON object. 351 * 352 * @param messageObject The JSON object containing an encoded representation 353 * of a forward access log message. It must not be 354 * {@code null}. 355 * 356 * @return The forward access log message that was created. 357 * 358 * @throws LogException If the provided JSON object cannot be decoded as a 359 * valid forward access log message. 360 */ 361 @NotNull() 362 private static JSONAccessLogMessage createForwardMessage( 363 @NotNull final JSONObject messageObject) 364 throws LogException 365 { 366 final AccessLogOperationType operationType = 367 getOperationType(messageObject); 368 switch (operationType) 369 { 370 case ABANDON: 371 return new JSONAbandonForwardAccessLogMessage(messageObject); 372 case ADD: 373 return new JSONAddForwardAccessLogMessage(messageObject); 374 case BIND: 375 return new JSONBindForwardAccessLogMessage(messageObject); 376 case COMPARE: 377 return new JSONCompareForwardAccessLogMessage(messageObject); 378 case DELETE: 379 return new JSONDeleteForwardAccessLogMessage(messageObject); 380 case EXTENDED: 381 return new JSONExtendedForwardAccessLogMessage(messageObject); 382 case MODIFY: 383 return new JSONModifyForwardAccessLogMessage(messageObject); 384 case MODDN: 385 return new JSONModifyDNForwardAccessLogMessage(messageObject); 386 case SEARCH: 387 return new JSONSearchForwardAccessLogMessage(messageObject); 388 case UNBIND: 389 default: 390 final String messageStr = messageObject.toSingleLineString(); 391 throw new LogException(messageStr, 392 ERR_JSON_ACCESS_LOG_READER_UNSUPPORTED_FORWARD_OP_TYPE.get( 393 messageStr, operationType.getLogIdentifier())); 394 } 395 } 396 397 398 399 /** 400 * Creates a new forward failed access log message from the provided JSON 401 * object. 402 * 403 * @param messageObject The JSON object containing an encoded representation 404 * of a forward failed access log message. It must not 405 * be {@code null}. 406 * 407 * @return The forward failed access log message that was created. 408 * 409 * @throws LogException If the provided JSON object cannot be decoded as a 410 * valid forward failed access log message. 411 */ 412 @NotNull() 413 private static JSONAccessLogMessage createForwardFailedMessage( 414 @NotNull final JSONObject messageObject) 415 throws LogException 416 { 417 final AccessLogOperationType operationType = 418 getOperationType(messageObject); 419 switch (operationType) 420 { 421 case ABANDON: 422 return new JSONAbandonForwardFailedAccessLogMessage(messageObject); 423 case ADD: 424 return new JSONAddForwardFailedAccessLogMessage(messageObject); 425 case BIND: 426 return new JSONBindForwardFailedAccessLogMessage(messageObject); 427 case COMPARE: 428 return new JSONCompareForwardFailedAccessLogMessage(messageObject); 429 case DELETE: 430 return new JSONDeleteForwardFailedAccessLogMessage(messageObject); 431 case EXTENDED: 432 return new JSONExtendedForwardFailedAccessLogMessage(messageObject); 433 case MODIFY: 434 return new JSONModifyForwardFailedAccessLogMessage(messageObject); 435 case MODDN: 436 return new JSONModifyDNForwardFailedAccessLogMessage(messageObject); 437 case SEARCH: 438 return new JSONSearchForwardFailedAccessLogMessage(messageObject); 439 case UNBIND: 440 default: 441 final String messageStr = messageObject.toSingleLineString(); 442 throw new LogException(messageStr, 443 ERR_JSON_ACCESS_LOG_READER_UNSUPPORTED_FORWARD_FAILED_OP_TYPE.get( 444 messageStr, operationType.getLogIdentifier())); 445 } 446 } 447 448 449 450 /** 451 * Creates a new assurance complete access log message from the provided JSON 452 * object. 453 * 454 * @param messageObject The JSON object containing an encoded representation 455 * of a assurance complete access log message. It must 456 * not be {@code null}. 457 * 458 * @return The assurance complete access log message that was created. 459 * 460 * @throws LogException If the provided JSON object cannot be decoded as a 461 * valid assurance complete access log message. 462 */ 463 @NotNull() 464 private static JSONAccessLogMessage createAssuranceCompleteMessage( 465 @NotNull final JSONObject messageObject) 466 throws LogException 467 { 468 final AccessLogOperationType operationType = 469 getOperationType(messageObject); 470 switch (operationType) 471 { 472 case ADD: 473 return new JSONAddAssuranceCompletedAccessLogMessage(messageObject); 474 case DELETE: 475 return new JSONDeleteAssuranceCompletedAccessLogMessage(messageObject); 476 case MODIFY: 477 return new JSONModifyAssuranceCompletedAccessLogMessage(messageObject); 478 case MODDN: 479 return new JSONModifyDNAssuranceCompletedAccessLogMessage( 480 messageObject); 481 case ABANDON: 482 case BIND: 483 case COMPARE: 484 case EXTENDED: 485 case SEARCH: 486 case UNBIND: 487 default: 488 final String messageStr = messageObject.toSingleLineString(); 489 throw new LogException(messageStr, 490 ERR_JSON_ACCESS_LOG_READER_UNSUPPORTED_ASSURANCE_COMPLETED_OP_TYPE. 491 get(messageStr, operationType.getLogIdentifier())); 492 } 493 } 494 495 496 497 /** 498 * Determines the operation type for the JSON-formatted access log message 499 * encoded in the provided JSON object. 500 * 501 * @param messageObject The JSON object containing an encoded representation 502 * of an access log message. It must not be 503 * {@code null}. 504 * 505 * @return The operation type extracted from the provided JSON object. 506 * 507 * @throws LogException If it is not possible to extract a valid operation 508 * type from the provided JSON object. 509 */ 510 @NotNull() 511 private static AccessLogOperationType getOperationType( 512 @NotNull final JSONObject messageObject) 513 throws LogException 514 { 515 final String opTypeStr = messageObject.getFieldAsString( 516 JSONFormattedAccessLogFields.OPERATION_TYPE.getFieldName()); 517 if (opTypeStr == null) 518 { 519 final String messageStr = messageObject.toSingleLineString(); 520 throw new LogException(messageStr, 521 ERR_JSON_ACCESS_LOG_READER_MISSING_OPERATION_TYPE.get(messageStr, 522 JSONFormattedAccessLogFields.OPERATION_TYPE.getFieldName())); 523 } 524 525 final AccessLogOperationType opType = 526 AccessLogOperationType.forName(opTypeStr); 527 if (opType == null) 528 { 529 final String messageStr = messageObject.toSingleLineString(); 530 throw new LogException(messageStr, 531 ERR_JSON_ACCESS_LOG_READER_UNSUPPORTED_OPERATION_TYPE.get(messageStr, 532 opTypeStr)); 533 } 534 535 return opType; 536 } 537 538 539 540 /** 541 * {@inheritDoc} 542 */ 543 @Override() 544 public void close() 545 throws IOException 546 { 547 jsonObjectReader.close(); 548 } 549}