001/*
002 * Copyright 2009-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2009-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) 2009-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;
037
038
039
040import java.util.Collections;
041import java.util.LinkedList;
042import java.util.List;
043import java.util.StringTokenizer;
044
045import com.unboundid.ldap.sdk.ResultCode;
046import com.unboundid.util.NotMutable;
047import com.unboundid.util.NotNull;
048import com.unboundid.util.Nullable;
049import com.unboundid.util.ThreadSafety;
050import com.unboundid.util.ThreadSafetyLevel;
051
052
053
054/**
055 * This class provides a data structure that holds information about a log
056 * message that may appear in the Directory Server access log about the result
057 * of a search operation processed by the Directory Server.
058 * <BR>
059 * <BLOCKQUOTE>
060 *   <B>NOTE:</B>  This class, and other classes within the
061 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
062 *   supported for use against Ping Identity, UnboundID, and
063 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
064 *   for proprietary functionality or for external specifications that are not
065 *   considered stable or mature enough to be guaranteed to work in an
066 *   interoperable way with other types of LDAP servers.
067 * </BLOCKQUOTE>
068 */
069@NotMutable()
070@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
071public final class SearchResultAccessLogMessage
072       extends SearchRequestAccessLogMessage
073       implements OperationResultAccessLogMessage
074{
075  /**
076   * The serial version UID for this serializable class.
077   */
078  private static final long serialVersionUID = 7181644154168110011L;
079
080
081
082  // Indicates whether the search was unindexed.
083  @Nullable private final Boolean isUnindexed;
084
085  // Indicates whether the any uncached data was accessed in the course of
086  // processing this operation.
087  @Nullable private final Boolean uncachedDataAccessed;
088
089  // The processing time for the operation.
090  @Nullable private final Double processingTime;
091
092  // The queue time for the operation.
093  @Nullable private final Double queueTime;
094
095  // The list of indexes for which keys near the index entry limit were accessed
096  // while processing the operation.
097  @NotNull private final List<String> indexesWithKeysAccessedNearEntryLimit;
098
099  // The list of indexes for which keys over the index entry limit were accessed
100  // while processing the operation.
101  @NotNull private final List<String> indexesWithKeysAccessedOverEntryLimit;
102
103  // The list of privileges required for processing the operation that the
104  // requester did not have.
105  @NotNull private final List<String> missingPrivileges;
106
107  // The list of privileges used during the course of processing the operation
108  // before an alternate authorization identity was assigned.
109  @NotNull private final List<String> preAuthZUsedPrivileges;
110
111  // The list of referral URLs for the operation.
112  @NotNull private final List<String> referralURLs;
113
114  // The list of response control OIDs for the operation.
115  @NotNull private final List<String> responseControlOIDs;
116
117  // The list of servers accessed while processing the operation.
118  @NotNull private final List<String> serversAccessed;
119
120  // The list of privileges used during the course of processing the operation.
121  @NotNull private final List<String> usedPrivileges;
122
123  // The number of entries returned to the client.
124  @Nullable private final Long entriesReturned;
125
126  // The number of intermediate response messages returned to the client.
127  @Nullable private final Long intermediateResponsesReturned;
128
129  // The result code for the operation.
130  @Nullable private final ResultCode resultCode;
131
132  // Additional information about the operation result.
133  @Nullable private final String additionalInformation;
134
135  // The alternate authorization DN for the operation.
136  @Nullable private final String authzDN;
137
138  // The diagnostic message for the operation.
139  @Nullable private final String diagnosticMessage;
140
141  // The intermediate client result for the operation.
142  @Nullable private final String intermediateClientResult;
143
144  // The matched DN for the operation.
145  @Nullable private final String matchedDN;
146
147  // The port of the backend server to which the request has been forwarded.
148  @Nullable private final Integer targetPort;
149
150  // The address of the backend server to which the request has been forwarded.
151  @Nullable private final String targetHost;
152
153  // The protocol used to forward the request to the backend server.
154  @Nullable private final String targetProtocol;
155
156
157
158  /**
159   * Creates a new search result access log message from the provided message
160   * string.
161   *
162   * @param  s  The string to be parsed as a search result access log message.
163   *
164   * @throws  LogException  If the provided string cannot be parsed as a valid
165   *                        log message.
166   */
167  public SearchResultAccessLogMessage(@NotNull final String s)
168         throws LogException
169  {
170    this(new LogMessage(s));
171  }
172
173
174
175  /**
176   * Creates a new search result access log message from the provided log
177   * message.
178   *
179   * @param  m  The log message to be parsed as a search result access log
180   *            message.
181   */
182  public SearchResultAccessLogMessage(@NotNull final LogMessage m)
183  {
184    super(m);
185
186    diagnosticMessage = getNamedValue("message");
187    additionalInformation = getNamedValue("additionalInfo");
188    matchedDN = getNamedValue("matchedDN");
189    processingTime = getNamedValueAsDouble("etime");
190    queueTime = getNamedValueAsDouble("qtime");
191    intermediateClientResult = getNamedValue("from");
192    entriesReturned = getNamedValueAsLong("entriesReturned");
193    isUnindexed = getNamedValueAsBoolean("unindexed");
194    authzDN = getNamedValue("authzDN");
195    targetHost = getNamedValue("targetHost");
196    targetPort = getNamedValueAsInteger("targetPort");
197    targetProtocol = getNamedValue("targetProtocol");
198
199    intermediateResponsesReturned =
200         getNamedValueAsLong("intermediateResponsesReturned");
201
202    final Integer rcInteger = getNamedValueAsInteger("resultCode");
203    if (rcInteger == null)
204    {
205      resultCode = null;
206    }
207    else
208    {
209      resultCode = ResultCode.valueOf(rcInteger);
210    }
211
212    final String refStr = getNamedValue("referralURLs");
213    if ((refStr == null) || refStr.isEmpty())
214    {
215      referralURLs = Collections.emptyList();
216    }
217    else
218    {
219      final LinkedList<String> refs = new LinkedList<>();
220      int startPos = 0;
221      while (true)
222      {
223        final int commaPos = refStr.indexOf(",ldap", startPos);
224        if (commaPos < 0)
225        {
226          refs.add(refStr.substring(startPos));
227          break;
228        }
229        else
230        {
231          refs.add(refStr.substring(startPos, commaPos));
232          startPos = commaPos + 1;
233        }
234      }
235      referralURLs = Collections.unmodifiableList(refs);
236    }
237
238    final String controlStr = getNamedValue("responseControls");
239    if (controlStr == null)
240    {
241      responseControlOIDs = Collections.emptyList();
242    }
243    else
244    {
245      final LinkedList<String> controlList = new LinkedList<>();
246      final StringTokenizer t = new StringTokenizer(controlStr, ",");
247      while (t.hasMoreTokens())
248      {
249        controlList.add(t.nextToken());
250      }
251      responseControlOIDs = Collections.unmodifiableList(controlList);
252    }
253
254    final String serversAccessedStr = getNamedValue("serversAccessed");
255    if ((serversAccessedStr == null) || serversAccessedStr.isEmpty())
256    {
257      serversAccessed = Collections.emptyList();
258    }
259    else
260    {
261      final LinkedList<String> servers = new LinkedList<>();
262      final StringTokenizer tokenizer =
263           new StringTokenizer(serversAccessedStr, ",");
264      while (tokenizer.hasMoreTokens())
265      {
266        servers.add(tokenizer.nextToken());
267      }
268      serversAccessed = Collections.unmodifiableList(servers);
269    }
270
271    uncachedDataAccessed = getNamedValueAsBoolean("uncachedDataAccessed");
272
273    final String usedPrivilegesStr = getNamedValue("usedPrivileges");
274    if ((usedPrivilegesStr == null) || usedPrivilegesStr.isEmpty())
275    {
276      usedPrivileges = Collections.emptyList();
277    }
278    else
279    {
280      final LinkedList<String> privileges = new LinkedList<>();
281      final StringTokenizer tokenizer =
282           new StringTokenizer(usedPrivilegesStr, ",");
283      while (tokenizer.hasMoreTokens())
284      {
285        privileges.add(tokenizer.nextToken());
286      }
287      usedPrivileges = Collections.unmodifiableList(privileges);
288    }
289
290    final String preAuthZUsedPrivilegesStr =
291         getNamedValue("preAuthZUsedPrivileges");
292    if ((preAuthZUsedPrivilegesStr == null) ||
293         preAuthZUsedPrivilegesStr.isEmpty())
294    {
295      preAuthZUsedPrivileges = Collections.emptyList();
296    }
297    else
298    {
299      final LinkedList<String> privileges = new LinkedList<>();
300      final StringTokenizer tokenizer =
301           new StringTokenizer(preAuthZUsedPrivilegesStr, ",");
302      while (tokenizer.hasMoreTokens())
303      {
304        privileges.add(tokenizer.nextToken());
305      }
306      preAuthZUsedPrivileges = Collections.unmodifiableList(privileges);
307    }
308
309    final String missingPrivilegesStr = getNamedValue("missingPrivileges");
310    if ((missingPrivilegesStr == null) || missingPrivilegesStr.isEmpty())
311    {
312      missingPrivileges = Collections.emptyList();
313    }
314    else
315    {
316      final LinkedList<String> privileges = new LinkedList<>();
317      final StringTokenizer tokenizer =
318           new StringTokenizer(missingPrivilegesStr, ",");
319      while (tokenizer.hasMoreTokens())
320      {
321        privileges.add(tokenizer.nextToken());
322      }
323      missingPrivileges = Collections.unmodifiableList(privileges);
324    }
325
326    final String indexesNearLimitStr =
327         getNamedValue("indexesWithKeysAccessedNearEntryLimit");
328    if ((indexesNearLimitStr == null) || indexesNearLimitStr.isEmpty())
329    {
330      indexesWithKeysAccessedNearEntryLimit = Collections.emptyList();
331    }
332    else
333    {
334      final LinkedList<String> indexes = new LinkedList<>();
335      final StringTokenizer tokenizer =
336           new StringTokenizer(indexesNearLimitStr, ",");
337      while (tokenizer.hasMoreTokens())
338      {
339        indexes.add(tokenizer.nextToken());
340      }
341      indexesWithKeysAccessedNearEntryLimit =
342           Collections.unmodifiableList(indexes);
343    }
344
345    final String indexesOverLimitStr =
346         getNamedValue("indexesWithKeysAccessedExceedingEntryLimit");
347    if ((indexesOverLimitStr == null) || indexesOverLimitStr.isEmpty())
348    {
349      indexesWithKeysAccessedOverEntryLimit = Collections.emptyList();
350    }
351    else
352    {
353      final LinkedList<String> indexes = new LinkedList<>();
354      final StringTokenizer tokenizer =
355           new StringTokenizer(indexesOverLimitStr, ",");
356      while (tokenizer.hasMoreTokens())
357      {
358        indexes.add(tokenizer.nextToken());
359      }
360      indexesWithKeysAccessedOverEntryLimit =
361           Collections.unmodifiableList(indexes);
362    }
363  }
364
365
366
367  /**
368   * Retrieves the result code for the operation.
369   *
370   * @return  The result code for the operation, or {@code null} if it is not
371   *          included in the log message.
372   */
373  @Override()
374  @Nullable()
375  public ResultCode getResultCode()
376  {
377    return resultCode;
378  }
379
380
381
382  /**
383   * Retrieves the diagnostic message for the operation.
384   *
385   * @return  The diagnostic message for the operation, or {@code null} if it is
386   *          not included in the log message.
387   */
388  @Override()
389  @Nullable()
390  public String getDiagnosticMessage()
391  {
392    return diagnosticMessage;
393  }
394
395
396
397  /**
398   * Retrieves a message with additional information about the result of the
399   * operation.
400   *
401   * @return  A message with additional information about the result of the
402   *          operation, or {@code null} if it is not included in the log
403   *          message.
404   */
405  @Override()
406  @Nullable()
407  public String getAdditionalInformation()
408  {
409    return additionalInformation;
410  }
411
412
413
414  /**
415   * Retrieves the matched DN for the operation.
416   *
417   * @return  The matched DN for the operation, or {@code null} if it is not
418   *          included in the log message.
419   */
420  @Override()
421  @Nullable()
422  public String getMatchedDN()
423  {
424    return matchedDN;
425  }
426
427
428
429  /**
430   * Retrieves the list of referral URLs for the operation.
431   *
432   * @return  The list of referral URLs for the operation, or an empty list if
433   *          it is not included in the log message.
434   */
435  @Override()
436  @NotNull()
437  public List<String> getReferralURLs()
438  {
439    return referralURLs;
440  }
441
442
443
444  /**
445   * Retrieves the number of intermediate response messages returned in the
446   * course of processing the operation.
447   *
448   * @return  The number of intermediate response messages returned to the
449   *          client in the course of processing the operation, or {@code null}
450   *          if it is not included in the log message.
451   */
452  @Override()
453  @Nullable()
454  public Long getIntermediateResponsesReturned()
455  {
456    return intermediateResponsesReturned;
457  }
458
459
460
461  /**
462   * Retrieves the length of time in milliseconds required to process the
463   * operation.
464   *
465   * @return  The length of time in milliseconds required to process the
466   *          operation, or {@code null} if it is not included in the log
467   *          message.
468   */
469  @Override()
470  @Nullable()
471  public Double getProcessingTimeMillis()
472  {
473    return processingTime;
474  }
475
476
477
478  /**
479   * Retrieves the length of time in milliseconds the operation was required to
480   * wait on the work queue.
481   *
482   * @return  The length of time in milliseconds the operation was required to
483   *          wait on the work queue, or {@code null} if it is not included in
484   *          the log message.
485   */
486  @Override()
487  @Nullable()
488  public Double getQueueTimeMillis()
489  {
490    return queueTime;
491  }
492
493
494
495  /**
496   * Retrieves the OIDs of any response controls contained in the log message.
497   *
498   * @return  The OIDs of any response controls contained in the log message, or
499   *          an empty list if it is not included in the log message.
500   */
501  @Override()
502  @NotNull()
503  public List<String> getResponseControlOIDs()
504  {
505    return responseControlOIDs;
506  }
507
508
509
510  /**
511   * Retrieves a list of the additional servers that were accessed in the course
512   * of processing the operation.  For example, if the access log message is
513   * from a Directory Proxy Server instance, then this may contain a list of the
514   * backend servers used to process the operation.
515   *
516   * @return  A list of the additional servers that were accessed in the course
517   *          of processing the operation, or an empty list if it is not
518   *          included in the log message.
519   */
520  @Override()
521  @NotNull()
522  public List<String> getServersAccessed()
523  {
524    return serversAccessed;
525  }
526
527
528
529  /**
530   * Indicates whether the server accessed any uncached data in the course of
531   * processing the operation.
532   *
533   * @return  {@code true} if the server was known to access uncached data in
534   *          the course of processing the operation, {@code false} if the
535   *          server was known not to access uncached data, or {@code null} if
536   *          it is not included in the log message (and the server likely did
537   *          not access uncached data).
538   */
539  @Nullable()
540  public Boolean getUncachedDataAccessed()
541  {
542    return uncachedDataAccessed;
543  }
544
545
546
547  /**
548   * Retrieves the content of the intermediate client result for the
549   * operation.
550   *
551   * @return  The content of the intermediate client result for the operation,
552   *          or {@code null} if it is not included in the log message.
553   */
554  @Override()
555  @Nullable()
556  public String getIntermediateClientResult()
557  {
558    return intermediateClientResult;
559  }
560
561
562
563  /**
564   * Retrieves the number of entries returned to the client.
565   *
566   * @return  The number of entries returned to the client, or {@code null} if
567   *          it is not included in the log message.
568   */
569  @Nullable()
570  public Long getEntriesReturned()
571  {
572    return entriesReturned;
573  }
574
575
576
577  /**
578   * Indicates whether the search was unindexed.
579   *
580   * @return  {@code Boolean.TRUE} if the search was unindexed,
581   *          {@code Boolean.FALSE} if it was not, or {@code null} if it is not
582   *          included in the log message.
583   */
584  @Nullable()
585  public Boolean isUnindexed()
586  {
587    return isUnindexed;
588  }
589
590
591
592  /**
593   * Retrieves the alternate authorization DN for the operation.
594   *
595   * @return  The alternate authorization DN for the operation, or {@code null}
596   *          if it is not included in the log message.
597   */
598  @Nullable()
599  public String getAlternateAuthorizationDN()
600  {
601    return authzDN;
602  }
603
604
605
606  /**
607   * Retrieves the address of the backend server to which the request has been
608   * forwarded.
609   *
610   * @return  The address of the backend server to which the request has been
611   *          forwarded, or {@code null} if it is not included in the log
612   *          message.
613   */
614  @Nullable()
615  public String getTargetHost()
616  {
617    return targetHost;
618  }
619
620
621
622  /**
623   * Retrieves the port of the backend server to which the request has been
624   * forwarded.
625   *
626   * @return  The port of the backend server to which the request has been
627   *          forwarded, or {@code null} if it is not included in the log
628   *          message.
629   */
630  @Nullable()
631  public Integer getTargetPort()
632  {
633    return targetPort;
634  }
635
636
637
638  /**
639   * Retrieves the protocol used to forward the request to the backend server.
640   *
641   * @return  The protocol used to forward the request to the backend server, or
642   *          {@code null} if it is not included in the log message.
643   */
644  @Nullable()
645  public String getTargetProtocol()
646  {
647    return targetProtocol;
648  }
649
650
651
652  /**
653   * Retrieves the names of any privileges used during the course of processing
654   * the operation.
655   *
656   * @return  The names of any privileges used during the course of processing
657   *          the operation, or an empty list if no privileges were used or this
658   *          is not included in the log message.
659   */
660  @NotNull()
661  public List<String> getUsedPrivileges()
662  {
663    return usedPrivileges;
664  }
665
666
667
668  /**
669   * Retrieves the names of any privileges used during the course of processing
670   * the operation before an alternate authorization identity was assigned.
671   *
672   * @return  The names of any privileges used during the course of processing
673   *          the operation before an alternate authorization identity was
674   *          assigned, or an empty list if no privileges were used or this is
675   *          not included in the log message.
676   */
677  @NotNull()
678  public List<String> getPreAuthorizationUsedPrivileges()
679  {
680    return preAuthZUsedPrivileges;
681  }
682
683
684
685  /**
686   * Retrieves the names of any privileges that would have been required for
687   * processing the operation but that the requester did not have.
688   *
689   * @return  The names of any privileges that would have been required for
690   *          processing the operation but that the requester did not have, or
691   *          an empty list if there were no missing privileges or this is not
692   *          included in the log message.
693   */
694  @NotNull()
695  public List<String> getMissingPrivileges()
696  {
697    return missingPrivileges;
698  }
699
700
701
702  /**
703   * Retrieves the names of any indexes for which one or more keys near
704   * (typically, within 80% of) the index entry limit were accessed while
705   * processing the operation.
706   *
707   * @return  The names of any indexes for which one or more keys near the index
708   *          entry limit were accessed while processing the operation, or an
709   *          empty list if no such index keys were accessed, or if this is not
710   *          included in the log message.
711   */
712  @NotNull()
713  public List<String> getIndexesWithKeysAccessedNearEntryLimit()
714  {
715    return indexesWithKeysAccessedNearEntryLimit;
716  }
717
718
719
720  /**
721   * Retrieves the names of any indexes for which one or more keys over the
722   * index entry limit were accessed while processing the operation.
723   *
724   * @return  The names of any indexes for which one or more keys over the index
725   *          entry limit were accessed while processing the operation, or an
726   *          empty list if no such index keys were accessed, or if this is not
727   *          included in the log message.
728   */
729  @NotNull()
730  public List<String> getIndexesWithKeysAccessedOverEntryLimit()
731  {
732    return indexesWithKeysAccessedOverEntryLimit;
733  }
734
735
736
737  /**
738   * {@inheritDoc}
739   */
740  @Override()
741  @NotNull()
742  public AccessLogMessageType getMessageType()
743  {
744    return AccessLogMessageType.RESULT;
745  }
746}