001/*
002 * Copyright 2018-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2018-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) 2018-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.tasks;
037
038
039
040import java.util.ArrayList;
041import java.util.Arrays;
042import java.util.Collection;
043import java.util.Collections;
044import java.util.Date;
045import java.util.LinkedHashMap;
046import java.util.LinkedList;
047import java.util.List;
048import java.util.Map;
049import java.util.concurrent.TimeUnit;
050
051import com.unboundid.ldap.sdk.Attribute;
052import com.unboundid.ldap.sdk.Entry;
053import com.unboundid.ldap.sdk.LDAPException;
054import com.unboundid.ldap.sdk.LDAPURL;
055import com.unboundid.util.Debug;
056import com.unboundid.util.NotMutable;
057import com.unboundid.util.NotNull;
058import com.unboundid.util.Nullable;
059import com.unboundid.util.StaticUtils;
060import com.unboundid.util.ThreadSafety;
061import com.unboundid.util.ThreadSafetyLevel;
062import com.unboundid.util.args.ArgumentException;
063import com.unboundid.util.args.DurationArgument;
064
065import static com.unboundid.ldap.sdk.unboundidds.tasks.TaskMessages.*;
066
067
068
069/**
070 * This class defines a Directory Server task that simply sleeps for a specified
071 * length of time or until a given condition occurs.  It is primarily intended
072 * to act as a separator between other tasks in a dependency chain.
073 * <BR>
074 * <BLOCKQUOTE>
075 *   <B>NOTE:</B>  This class, and other classes within the
076 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
077 *   supported for use against Ping Identity, UnboundID, and
078 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
079 *   for proprietary functionality or for external specifications that are not
080 *   considered stable or mature enough to be guaranteed to work in an
081 *   interoperable way with other types of LDAP servers.
082 * </BLOCKQUOTE>
083 */
084@NotMutable()
085@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
086public final class DelayTask
087       extends Task
088{
089  /**
090   * The fully-qualified name of the Java class that is used for the delay task.
091   */
092  @NotNull static final String DELAY_TASK_CLASS =
093       "com.unboundid.directory.server.tasks.DelayTask";
094
095
096
097  /**
098   * The name of the attribute used to specify the length of time that the
099   * task should sleep.
100   */
101  @NotNull private static final String ATTR_SLEEP_DURATION =
102       "ds-task-delay-sleep-duration";
103
104
105
106  /**
107   * The name of the task attribute that indicates whether to wait for the work
108   * queue to become idle.
109   */
110  @NotNull private static final String ATTR_WAIT_FOR_WORK_QUEUE_IDLE =
111       "ds-task-delay-duration-to-wait-for-work-queue-idle";
112
113
114
115  /**
116   * The name of the task attribute that provides a set of LDAP URLs to use to
117   * issue searches that are expected to eventually return entries.
118   */
119  @NotNull private static final String ATTR_SEARCH_URL =
120       "ds-task-delay-ldap-url-for-search-expected-to-return-entries";
121
122
123
124  /**
125   * The name of the task attribute that specifies the length of time between
126   * searches.
127   */
128  @NotNull private static final String ATTR_SEARCH_INTERVAL =
129       "ds-task-delay-search-interval";
130
131
132
133  /**
134   * The name of the task attribute that specifies the time limit for each
135   * search.
136   */
137  @NotNull private static final String ATTR_SEARCH_TIME_LIMIT =
138       "ds-task-delay-search-time-limit";
139
140
141
142  /**
143   * The name of the task attribute that specifies the total length of time to
144   * wait for each search to return one or more entries.
145   */
146  @NotNull private static final String ATTR_SEARCH_DURATION =
147       "ds-task-delay-duration-to-wait-for-search-to-return-entries";
148
149
150
151  /**
152   * The name of the task attribute that specifies the task return state to use
153   * if a timeout is encountered during processing.
154   */
155  @NotNull private static final String ATTR_TIMEOUT_RETURN_STATE =
156       "ds-task-delay-task-return-state-if-timeout-is-encountered";
157
158
159
160  /**
161   * The name of the object class used in delay task entries.
162   */
163  @NotNull private static final String OC_DELAY_TASK = "ds-task-delay";
164
165
166
167  /**
168   * The task property that will be used for the sleep duration.
169   */
170  @NotNull private static final TaskProperty PROPERTY_SLEEP_DURATION_MILLIS =
171     new TaskProperty(ATTR_SLEEP_DURATION,
172          INFO_DELAY_DISPLAY_NAME_SLEEP_DURATION.get(),
173          INFO_DELAY_DESCRIPTION_SLEEP_DURATION.get(), Long.class, false,
174          false, false);
175
176
177
178  /**
179   * The task property that will be used for the length of time to wait for the
180   * work queue to report that the server is idle.
181   */
182  @NotNull private static final TaskProperty
183       PROPERTY_WAIT_FOR_WORK_QUEUE_IDLE_MILLIS = new TaskProperty(
184            ATTR_WAIT_FOR_WORK_QUEUE_IDLE,
185            INFO_DELAY_DISPLAY_NAME_WAIT_FOR_WORK_QUEUE_IDLE.get(),
186            INFO_DELAY_DESCRIPTION_WAIT_FOR_WORK_QUEUE_IDLE.get(), Long.class,
187            false, false, false);
188
189
190
191  /**
192   * The task property that will be used to provide LDAP URLs for searches that
193   * are expected to eventually return entries.
194   */
195  @NotNull private static final TaskProperty PROPERTY_SEARCH_URL =
196     new TaskProperty(ATTR_SEARCH_URL,
197          INFO_DELAY_DISPLAY_NAME_SEARCH_URL.get(),
198          INFO_DELAY_DESCRIPTION_SEARCH_URL.get(), String.class, false, true,
199          false);
200
201
202
203  /**
204   * The task property that will be used to specify the length of time between
205   * searches.
206   */
207  @NotNull private static final TaskProperty PROPERTY_SEARCH_INTERVAL_MILLIS =
208     new TaskProperty(ATTR_SEARCH_INTERVAL,
209          INFO_DELAY_DISPLAY_NAME_SEARCH_INTERVAL.get(),
210          INFO_DELAY_DESCRIPTION_SEARCH_INTERVAL.get(), Long.class, false,
211          false, false);
212
213
214
215  /**
216   * The task property that will be used to specify the time limit for each
217   * search.
218   */
219  @NotNull private static final TaskProperty PROPERTY_SEARCH_TIME_LIMIT_MILLIS =
220     new TaskProperty(ATTR_SEARCH_TIME_LIMIT,
221          INFO_DELAY_DISPLAY_NAME_SEARCH_TIME_LIMIT.get(),
222          INFO_DELAY_DESCRIPTION_SEARCH_TIME_LIMIT.get(), Long.class, false,
223          false, false);
224
225
226
227  /**
228   * The task property that will be used to specify the total length of time
229   * allowed for a search to return entries.
230   */
231  @NotNull private static final TaskProperty PROPERTY_SEARCH_DURATION_MILLIS =
232     new TaskProperty(ATTR_SEARCH_DURATION,
233          INFO_DELAY_DISPLAY_NAME_SEARCH_DURATION.get(),
234          INFO_DELAY_DESCRIPTION_SEARCH_DURATION.get(), Long.class, false,
235          false, false);
236
237
238
239  /**
240   * The task property that will be used for the task return state if a timeout
241   * is encountered.
242   */
243  @NotNull private static final TaskProperty PROPERTY_TIMEOUT_RETURN_STATE =
244     new TaskProperty(ATTR_TIMEOUT_RETURN_STATE,
245          INFO_DELAY_DISPLAY_NAME_TIMEOUT_RETURN_STATE.get(),
246          INFO_DELAY_DESCRIPTION_TIMEOUT_RETURN_STATE.get(),
247          String.class, false, false, false,
248          new String[]
249          {
250            "STOPPED_BY_ERROR",
251            "STOPPED-BY-ERROR",
252            "COMPLETED_WITH_ERRORS",
253            "COMPLETED-WITH-ERRORS",
254            "COMPLETED_SUCCESSFULLY",
255            "COMPLETED-SUCCESSFULLY"
256          });
257
258
259
260  /**
261   * The serial version UID for this serializable class.
262   */
263  private static final long serialVersionUID = -639870096358259180L;
264
265
266
267  // A list of LDAP URLs that define searches that are expected to return
268  // entries.
269  @NotNull private final List<LDAPURL>
270       ldapURLsForSearchesExpectedToReturnEntries;
271
272  // The length of time, in milliseconds, between each search.
273  @Nullable private final Long millisBetweenSearches;
274
275  // The maximum length of time, in milliseconds, that the task should wait for
276  // the work queue to report that the server is idle.
277  @Nullable private final Long millisToWaitForWorkQueueToBecomeIdle;
278
279  // The maximum length of time, in milliseconds, to wait for a response to
280  // each search.
281  @Nullable private final Long searchTimeLimitMillis;
282
283  // The length of time, in milliseconds, that the task should sleep.
284  @Nullable private final Long sleepDurationMillis;
285
286  // The maximum length of time, in milliseconds, to wait for each search to
287  // return at least one entry.
288  @Nullable private final Long totalDurationMillisForEachLDAPURL;
289
290  // The task state that should be returned if a timeout is encountered during
291  // task processing.
292  @Nullable private final String taskStateIfTimeoutIsEncountered;
293
294
295
296  /**
297   * Creates a new, uninitialized delay task instance that should only be used
298   * for obtaining general information about this task, including the task name,
299   * description, and supported properties.  Attempts to use a task created with
300   * this constructor for any other reason will likely fail.
301   */
302  public DelayTask()
303  {
304    ldapURLsForSearchesExpectedToReturnEntries = null;
305    millisBetweenSearches = null;
306    millisToWaitForWorkQueueToBecomeIdle = null;
307    searchTimeLimitMillis = null;
308    sleepDurationMillis = null;
309    totalDurationMillisForEachLDAPURL = null;
310    taskStateIfTimeoutIsEncountered = null;
311  }
312
313
314
315  /**
316   * Creates a new delay task with the provided information.
317   *
318   * @param  sleepDurationMillis
319   *             The length of time, in milliseconds, that the task should
320   *             sleep.  This may be {@code null} if the task is intended to
321   *             wait for the work queue to become idle or searches to return
322   *             entries and no additional sleep is required.  If it is not
323   *             {@code null}, then it must be greater than zero.  If a sleep
324   *             duration is provided and the task should also wait for the work
325   *             queue to become idle or wait for search results, then the sleep
326   *             for this duration will occur after waiting for those other
327   *             conditions to be satisfied (or for a timeout to occur).
328   * @param  millisToWaitForWorkQueueToBecomeIdle
329   *              The length of time, in milliseconds, that the task should wait
330   *              for the server work queue to report that there are no pending
331   *              requests and all worker threads are idle.  This may be
332   *              {@code null} if the task should not wait for the work queue to
333   *              become idle.  If it is not {@code null}, then it must be
334   *              greater than zero.
335   * @param  ldapURLsForSearchesExpectedToReturnEntries
336   *              A list of LDAP URLs that provide criteria for search requests
337   *              that are eventually expected to return one or more entries.
338   *              This may be {@code null} or empty if the task should not
339   *              perform any such searches.  If this is non-empty, then the
340   *              {@code millisBetweenSearches},
341   *              {@code searchTimeLimitMillis}, and
342   *              {@code totalDurationMillisForEachLDAPURL} arguments must be
343   *              non-{@code null}.
344   * @param  millisBetweenSearches
345   *              The length of time, in milliseconds, between the individual
346   *              searches created from each of the provided LDAP URLs.  Each
347   *              search created from an LDAP URL will be repeated until it
348   *              returns at least one entry, or until the total length of time
349   *              processing that search meets or exceeds the value of the
350   *              {@code totalDurationMillisForEachSearch} argument.  If the
351   *              {@code ldapURLsForSearchesExpectedToReturnEntries} list is not
352   *              empty, then this must not be {@code null}.  If it is not
353   *              {@code null}, then it must be greater than zero.
354   * @param  searchTimeLimitMillis
355   *              The maximum length of time, in milliseconds, to wait for a
356   *              response to each individual search created from one of the
357   *              provided LDAP URLs.  If the
358   *              {@code ldapURLsForSearchesExpectedToReturnEntries} list is
359   *              not empty, then this must not be {@code null}.  If it is not
360   *              {@code null}, then it must be greater than zero.
361   * @param  totalDurationMillisForEachLDAPURL
362   *              The maximum length of time, in milliseconds, to wait for the
363   *              search criteria created from each of the provided LDAP URLs
364   *              to match at least one entry.  If the
365   *              {@code ldapURLsForSearchesExpectedToReturnEntries} list is
366   *              not empty, then this must not be {@code null}.  If it is not
367   *              {@code null}, then it must be greater than zero.
368   * @param  taskStateIfTimeoutIsEncountered
369   *              The task state that should be used if a timeout is encountered
370   *              while waiting for the work queue to become idle or while
371   *              waiting for search criteria created from an LDAP URL to match
372   *              at least one entry.  This may be {@code null} to indicate that
373   *              the server should determine the appropriate task state.  If it
374   *              is non-{@code null}, then the value must be one of
375   *              {@link TaskState#STOPPED_BY_ERROR},
376   *              {@link TaskState#COMPLETED_WITH_ERRORS}, or
377   *              {@link TaskState#COMPLETED_SUCCESSFULLY}.
378   *
379   * @throws  TaskException  If there is a problem with any of the provided
380   *                         arguments.
381   */
382  public DelayTask(@Nullable final Long sleepDurationMillis,
383       @Nullable final Long millisToWaitForWorkQueueToBecomeIdle,
384       @Nullable final Collection<LDAPURL>
385            ldapURLsForSearchesExpectedToReturnEntries,
386       @Nullable final Long millisBetweenSearches,
387       @Nullable final Long searchTimeLimitMillis,
388       @Nullable final Long totalDurationMillisForEachLDAPURL,
389       @Nullable final TaskState taskStateIfTimeoutIsEncountered)
390       throws TaskException
391  {
392    this(null, sleepDurationMillis, millisToWaitForWorkQueueToBecomeIdle,
393         ldapURLsForSearchesExpectedToReturnEntries, millisBetweenSearches,
394         searchTimeLimitMillis, totalDurationMillisForEachLDAPURL,
395         taskStateIfTimeoutIsEncountered, null, null, null, null, null, null,
396         null, null, null, null);
397  }
398
399
400
401  /**
402   * Creates a new delay task with the provided information.
403   *
404   * @param  taskID
405   *              The task ID to use for this task.  If it is {@code null} then
406   *              a UUID will be generated for use as the task ID.
407   * @param  sleepDurationMillis
408   *             The length of time, in milliseconds, that the task should
409   *             sleep.  This may be {@code null} if the task is intended to
410   *             wait for the work queue to become idle or searches to return
411   *             entries and no additional sleep is required.  If it is not
412   *             {@code null}, then it must be greater than zero.  If a sleep
413   *             duration is provided and the task should also wait for the work
414   *             queue to become idle or wait for search results, then the sleep
415   *             for this duration will occur after waiting for those other
416   *             conditions to be satisfied (or for a timeout to occur).
417   * @param  millisToWaitForWorkQueueToBecomeIdle
418   *              The length of time, in milliseconds, that the task should wait
419   *              for the server work queue to report that there are no pending
420   *              requests and all worker threads are idle.  This may be
421   *              {@code null} if the task should not wait for the work queue to
422   *              become idle.  If it is not {@code null}, then it must be
423   *              greater than zero.
424   * @param  ldapURLsForSearchesExpectedToReturnEntries
425   *              A list of LDAP URLs that provide criteria for search requests
426   *              that are eventually expected to return one or more entries.
427   *              This may be {@code null} or empty if the task should not
428   *              perform any such searches.  If this is non-empty, then the
429   *              {@code millisBetweenSearches},
430   *              {@code searchTimeLimitMillis}, and
431   *              {@code totalDurationMillisForEachLDAPURL} arguments must be
432   *              non-{@code null}.
433   * @param  millisBetweenSearches
434   *              The length of time, in milliseconds, between the individual
435   *              searches created from each of the provided LDAP URLs.  Each
436   *              search created from an LDAP URL will be repeated until it
437   *              returns at least one entry, or until the total length of time
438   *              processing that search meets or exceeds the value of the
439   *              {@code totalDurationMillisForEachSearch} argument.  If the
440   *              {@code ldapURLsForSearchesExpectedToReturnEntries} list is not
441   *              empty, then this must not be {@code null}.  If it is not
442   *              {@code null}, then it must be greater than zero.
443   * @param  searchTimeLimitMillis
444   *              The maximum length of time, in milliseconds, to wait for a
445   *              response to each individual search created from one of the
446   *              provided LDAP URLs.  If the
447   *              {@code ldapURLsForSearchesExpectedToReturnEntries} list is
448   *              not empty, then this must not be {@code null}.  If it is not
449   *              {@code null}, then it must be greater than zero.
450   * @param  totalDurationMillisForEachLDAPURL
451   *              The maximum length of time, in milliseconds, to wait for the
452   *              search criteria created from each of the provided LDAP URLs
453   *              to match at least one entry.  If the
454   *              {@code ldapURLsForSearchesExpectedToReturnEntries} list is
455   *              not empty, then this must not be {@code null}.  If it is not
456   *              {@code null}, then it must be greater than zero.
457   * @param  taskStateIfTimeoutIsEncountered
458   *              The task state that should be used if a timeout is encountered
459   *              while waiting for the work queue to become idle or while
460   *              waiting for search criteria created from an LDAP URL to match
461   *              at least one entry.  This may be {@code null} to indicate that
462   *              the server should determine the appropriate task state.  If it
463   *              is non-{@code null}, then the value must be one of
464   *              {@link TaskState#STOPPED_BY_ERROR},
465   *              {@link TaskState#COMPLETED_WITH_ERRORS}, or
466   *              {@link TaskState#COMPLETED_SUCCESSFULLY}.
467   * @param  scheduledStartTime
468   *              The time that this task should start running.
469   * @param  dependencyIDs
470   *              The list of task IDs that will be required to complete before
471   *              this task will be eligible to start.
472   * @param  failedDependencyAction
473   *              Indicates what action should be taken if any of the
474   *              dependencies for this task do not complete successfully.
475   * @param  notifyOnStart
476   *              The list of e-mail addresses of individuals that should be
477   *              notified when this task starts.
478   * @param  notifyOnCompletion
479   *              The list of e-mail addresses of individuals that should be
480   *              notified when this task completes.
481   * @param  notifyOnSuccess
482   *              The list of e-mail addresses of individuals that should be
483   *              notified if this task completes successfully.
484   * @param  notifyOnError
485   *              The list of e-mail addresses of individuals that should be
486   *              notified if this task does not complete successfully.
487   * @param  alertOnStart
488   *              Indicates whether the server should send an alert notification
489   *              when this task starts.
490   * @param  alertOnSuccess
491   *              Indicates whether the server should send an alert notification
492   *              if this task completes successfully.
493   * @param  alertOnError
494   *              Indicates whether the server should send an alert notification
495   *              if this task fails to complete successfully.
496   *
497   * @throws  TaskException  If there is a problem with any of the provided
498   *                         arguments.
499   */
500  public DelayTask(@Nullable final String taskID,
501       @Nullable final Long sleepDurationMillis,
502       @Nullable final Long millisToWaitForWorkQueueToBecomeIdle,
503       @Nullable final Collection<LDAPURL>
504            ldapURLsForSearchesExpectedToReturnEntries,
505       @Nullable final Long millisBetweenSearches,
506       @Nullable final Long searchTimeLimitMillis,
507       @Nullable final Long totalDurationMillisForEachLDAPURL,
508       @Nullable final TaskState taskStateIfTimeoutIsEncountered,
509       @Nullable final Date scheduledStartTime,
510       @Nullable final List<String> dependencyIDs,
511       @Nullable final FailedDependencyAction failedDependencyAction,
512       @Nullable final List<String> notifyOnStart,
513       @Nullable final List<String> notifyOnCompletion,
514       @Nullable final List<String> notifyOnSuccess,
515       @Nullable final List<String> notifyOnError,
516       @Nullable final Boolean alertOnStart,
517       @Nullable final Boolean alertOnSuccess,
518       @Nullable final Boolean alertOnError)
519       throws TaskException
520  {
521    super(taskID, DELAY_TASK_CLASS, scheduledStartTime, dependencyIDs,
522         failedDependencyAction, notifyOnStart, notifyOnCompletion,
523         notifyOnSuccess, notifyOnError, alertOnStart, alertOnSuccess,
524         alertOnError);
525
526    this.sleepDurationMillis = sleepDurationMillis;
527    this.millisToWaitForWorkQueueToBecomeIdle =
528         millisToWaitForWorkQueueToBecomeIdle;
529    this.millisBetweenSearches = millisBetweenSearches;
530    this.searchTimeLimitMillis = searchTimeLimitMillis;
531    this.totalDurationMillisForEachLDAPURL = totalDurationMillisForEachLDAPURL;
532
533    if (ldapURLsForSearchesExpectedToReturnEntries == null)
534    {
535      this.ldapURLsForSearchesExpectedToReturnEntries = Collections.emptyList();
536    }
537    else
538    {
539      this.ldapURLsForSearchesExpectedToReturnEntries =
540           Collections.unmodifiableList(
541                new ArrayList<>(ldapURLsForSearchesExpectedToReturnEntries));
542    }
543
544    if (taskStateIfTimeoutIsEncountered == null)
545    {
546      this.taskStateIfTimeoutIsEncountered = null;
547    }
548    else
549    {
550      switch (taskStateIfTimeoutIsEncountered)
551      {
552        case STOPPED_BY_ERROR:
553        case COMPLETED_WITH_ERRORS:
554        case COMPLETED_SUCCESSFULLY:
555          this.taskStateIfTimeoutIsEncountered =
556               taskStateIfTimeoutIsEncountered.name();
557          break;
558        default:
559          throw new TaskException(
560               ERR_DELAY_INVALID_TIMEOUT_STATE.get(
561                    TaskState.STOPPED_BY_ERROR.name(),
562                    TaskState.COMPLETED_WITH_ERRORS.name(),
563                    TaskState.COMPLETED_SUCCESSFULLY.name()));
564      }
565    }
566
567    if ((sleepDurationMillis != null) && (sleepDurationMillis <= 0L))
568    {
569      throw new TaskException(ERR_DELAY_INVALID_SLEEP_DURATION.get());
570    }
571
572    if ((millisToWaitForWorkQueueToBecomeIdle != null) &&
573       (millisToWaitForWorkQueueToBecomeIdle <= 0L))
574    {
575      throw new TaskException(ERR_DELAY_INVALID_WAIT_FOR_QUEUE_IDLE.get());
576    }
577
578    if ((millisBetweenSearches != null) && (millisBetweenSearches <= 0L))
579    {
580      throw new TaskException(ERR_DELAY_INVALID_SEARCH_INTERVAL.get());
581    }
582
583    if ((searchTimeLimitMillis != null) && (searchTimeLimitMillis <= 0L))
584    {
585      throw new TaskException(ERR_DELAY_INVALID_SEARCH_TIME_LIMIT.get());
586    }
587
588    if ((totalDurationMillisForEachLDAPURL != null) &&
589         (totalDurationMillisForEachLDAPURL <= 0L))
590    {
591      throw new TaskException(ERR_DELAY_INVALID_SEARCH_DURATION.get());
592    }
593
594    if (! this.ldapURLsForSearchesExpectedToReturnEntries.isEmpty())
595    {
596      if ((millisBetweenSearches == null) ||
597           (searchTimeLimitMillis == null) ||
598           (totalDurationMillisForEachLDAPURL == null))
599      {
600        throw new TaskException(ERR_DELAY_URL_WITHOUT_REQUIRED_PARAM.get());
601      }
602
603      if (millisBetweenSearches >= totalDurationMillisForEachLDAPURL)
604      {
605        throw new TaskException(ERR_DELAY_INVALID_SEARCH_INTERVAL.get());
606      }
607
608      if (searchTimeLimitMillis >= totalDurationMillisForEachLDAPURL)
609      {
610        throw new TaskException(ERR_DELAY_INVALID_SEARCH_TIME_LIMIT.get());
611      }
612    }
613  }
614
615
616
617  /**
618   * Creates a new delay task from the provided entry.
619   *
620   * @param  entry  The entry to use to create this delay task.
621   *
622   * @throws  TaskException  If the provided entry cannot be parsed as an delay
623   *                         task entry.
624   */
625  public DelayTask(@NotNull final Entry entry)
626         throws TaskException
627  {
628    super(entry);
629
630
631    // Get the name of the task state to use if a timeout occurs during task
632    // processing.
633    taskStateIfTimeoutIsEncountered =
634         entry.getAttributeValue(ATTR_TIMEOUT_RETURN_STATE);
635
636
637    // Parse the duration attributes.
638    sleepDurationMillis = parseDuration(entry, ATTR_SLEEP_DURATION);
639    millisToWaitForWorkQueueToBecomeIdle =
640         parseDuration(entry,ATTR_WAIT_FOR_WORK_QUEUE_IDLE);
641    millisBetweenSearches = parseDuration(entry, ATTR_SEARCH_INTERVAL);
642    searchTimeLimitMillis = parseDuration(entry, ATTR_SEARCH_TIME_LIMIT);
643    totalDurationMillisForEachLDAPURL =
644         parseDuration(entry, ATTR_SEARCH_DURATION);
645
646
647    // Parse the set of LDAP URLs.
648    final String[] urlStrings = entry.getAttributeValues(ATTR_SEARCH_URL);
649    if (urlStrings == null)
650    {
651      ldapURLsForSearchesExpectedToReturnEntries = Collections.emptyList();
652    }
653    else
654    {
655      final ArrayList<LDAPURL> urls = new ArrayList<>(urlStrings.length);
656      for (final String s : urlStrings)
657      {
658        try
659        {
660          urls.add(new LDAPURL(s));
661        }
662        catch (final LDAPException e)
663        {
664          Debug.debugException(e);
665          throw new TaskException(
666               ERR_DELAY_ENTRY_MALFORMED_URL.get(ATTR_SEARCH_URL, s,
667                    e.getMessage()),
668               e);
669        }
670      }
671
672      ldapURLsForSearchesExpectedToReturnEntries =
673           Collections.unmodifiableList(urls);
674    }
675  }
676
677
678
679  /**
680   * Retrieves the value of the specified attribute from the given entry and
681   * parses its value as a duration.
682   *
683   * @param  entry          The entry from which to retrieve the attribute.
684   * @param  attributeName  The name of the attribute containing the value to
685   *                        parse.  It must not be {@code null}.
686   *
687   * @return  The number of milliseconds in the duration represented by the
688   *          value of the specified attribute, or {@code null} if the attribute
689   *          was not present in the entry.
690   *
691   * @throws  TaskException  If the attribute value cannot be parsed as a
692   *                         duration.
693   */
694  @Nullable()
695  private static Long parseDuration(@NotNull final Entry entry,
696                                    @NotNull final String attributeName)
697          throws TaskException
698  {
699    final String value = entry.getAttributeValue(attributeName);
700    if (value == null)
701    {
702      return null;
703    }
704
705    try
706    {
707      return DurationArgument.parseDuration(value, TimeUnit.MILLISECONDS);
708    }
709    catch (final ArgumentException e)
710    {
711      throw new TaskException(
712           ERR_DELAY_CANNOT_PARSE_ATTR_VALUE_AS_DURATION.get(attributeName,
713                e.getMessage()),
714           e);
715    }
716  }
717
718
719
720  /**
721   * Creates a new delay task from the provided set of task properties.
722   *
723   * @param  properties  The set of task properties and their corresponding
724   *                     values to use for the task.  It must not be
725   *                     {@code null}.
726   *
727   * @throws  TaskException  If the provided set of properties cannot be used to
728   *                         create a valid delay task.
729   */
730  public DelayTask(@NotNull final Map<TaskProperty,List<Object>> properties)
731         throws TaskException
732  {
733    super(DELAY_TASK_CLASS, properties);
734
735    Long searchDuration = null;
736    Long searchInterval = null;
737    Long searchTimeLimit = null;
738    Long sleepDuration = null;
739    Long workQueueWaitTime = null;
740    String timeoutReturnState = null;
741    final List<LDAPURL> urls = new ArrayList<>(10);
742    for (final Map.Entry<TaskProperty,List<Object>> entry :
743         properties.entrySet())
744    {
745      final TaskProperty p = entry.getKey();
746      final String attrName = StaticUtils.toLowerCase(p.getAttributeName());
747      final List<Object> values = entry.getValue();
748      switch (attrName)
749      {
750        case ATTR_SLEEP_DURATION:
751          sleepDuration = parseLong(p, values, null);
752          break;
753        case ATTR_WAIT_FOR_WORK_QUEUE_IDLE:
754          workQueueWaitTime = parseLong(p, values, null);
755          break;
756        case ATTR_SEARCH_URL:
757          for (final String urlString :
758               parseStrings(p, values, StaticUtils.NO_STRINGS))
759          {
760            try
761            {
762              urls.add(new LDAPURL(urlString));
763            }
764            catch (final LDAPException e)
765            {
766              Debug.debugException(e);
767              throw new TaskException(
768                   ERR_DELAY_ENTRY_MALFORMED_URL.get(ATTR_SEARCH_URL, urlString,
769                        e.getMessage()),
770                   e);
771            }
772          }
773          break;
774        case ATTR_SEARCH_INTERVAL:
775          searchInterval = parseLong(p, values, null);
776          break;
777        case ATTR_SEARCH_TIME_LIMIT:
778          searchTimeLimit = parseLong(p, values, null);
779          break;
780        case ATTR_SEARCH_DURATION:
781          searchDuration = parseLong(p, values, null);
782          break;
783        case ATTR_TIMEOUT_RETURN_STATE:
784          timeoutReturnState = parseString(p, values, null);
785          break;
786      }
787    }
788
789    sleepDurationMillis = sleepDuration;
790    millisToWaitForWorkQueueToBecomeIdle = workQueueWaitTime;
791    ldapURLsForSearchesExpectedToReturnEntries =
792         Collections.unmodifiableList(urls);
793    millisBetweenSearches = searchInterval;
794    searchTimeLimitMillis = searchTimeLimit;
795    totalDurationMillisForEachLDAPURL = searchDuration;
796    taskStateIfTimeoutIsEncountered = timeoutReturnState;
797  }
798
799
800
801  /**
802   * {@inheritDoc}
803   */
804  @Override()
805  @NotNull()
806  public String getTaskName()
807  {
808    return INFO_TASK_NAME_DELAY.get();
809  }
810
811
812
813  /**
814   * {@inheritDoc}
815   */
816  @Override()
817  @NotNull()
818  public String getTaskDescription()
819  {
820    return INFO_TASK_DESCRIPTION_DELAY.get();
821  }
822
823
824
825  /**
826   * Retrieves the length of time, in milliseconds, that the task should sleep.
827   *
828   * @return  The length of time, in milliseconds, that the task should sleep,
829   *          or {@code null} if the task should not sleep for a specified
830   *          period of time.
831   */
832  @Nullable()
833  public Long getSleepDurationMillis()
834  {
835    return sleepDurationMillis;
836  }
837
838
839
840  /**
841   * Retrieves the length of time, in milliseconds, that the task should wait
842   * for the server work queue to report that there are no pending requests and
843   * all worker threads are idle.
844   *
845   * @return  The length of time, in milliseconds, that the task should wait for
846   *          the server work queue to report that it is idle, or {@code null}
847   *          if the task should not wait for the work queue to be idle
848   */
849  @Nullable()
850  public Long getMillisToWaitForWorkQueueToBecomeIdle()
851  {
852    return millisToWaitForWorkQueueToBecomeIdle;
853  }
854
855
856
857  /**
858   * Retrieves a list of LDAP URLs that provide criteria for search requests
859   * that are eventually expected to return one or more entries.
860   *
861   * @return  A list of LDAP URLs that provide criteria for search requests that
862   *          are eventually expected to return one or more entries, or an empty
863   *          list if no searches are to be performed.
864   */
865  @NotNull()
866  public List<LDAPURL> getLDAPURLsForSearchesExpectedToReturnEntries()
867  {
868    return ldapURLsForSearchesExpectedToReturnEntries;
869  }
870
871
872
873  /**
874   * Retrieves the length of time, in milliseconds, between the individual
875   * searches created from each of the provided LDAP URLs.  Each search created
876   * from an LDAP URL will be repeated until it returns at least one entry, or
877   * until the total length of processing that search meets or exceeds the value
878   * returned by the {@link #getTotalDurationMillisForEachLDAPURL()} method.
879   *
880   * @return  The length of time, in milliseconds, between the individual
881   *          searches created from each of the provided LDAP URLs, or
882   *          {@code null} if no searches are to be performed.
883   */
884  @Nullable()
885  public Long getMillisBetweenSearches()
886  {
887    return millisBetweenSearches;
888  }
889
890
891
892  /**
893   * Retrieves the maximum length of time, in milliseconds, to wait for a
894   * response to each individual search created from one of the provided LDAP
895   * URLs.
896   *
897   * @return  The maximum length of time, in milliseconds, to wait for a
898   *          response to each individual search created from one of the
899   *          provided LDAP URLs, or {@code null} if no searches are to be
900   *          performed.
901   */
902  @Nullable()
903  public Long getSearchTimeLimitMillis()
904  {
905    return searchTimeLimitMillis;
906  }
907
908
909
910  /**
911   * Retrieves the maximum length of time, in milliseconds, to wait for the
912   * search criteria created from each of the provided LDAP URLs to match at
913   * least one entry.
914   *
915   * @return  The maximum length of time, in milliseconds, to wait for the
916   *          search criteria created from each of the provided LDAP URLs to
917   *          match at least one entry, or {@code null} if no searches are to be
918   *          performed.
919   */
920  @Nullable()
921  public Long getTotalDurationMillisForEachLDAPURL()
922  {
923    return totalDurationMillisForEachLDAPURL;
924  }
925
926
927
928  /**
929   * Retrieves the name of the task state that should be used if a timeout is
930   * encountered while waiting for the work queue to become idle or while
931   * waiting for search criteria created from an LDAP URL to match at least one
932   * entry.
933   *
934   * @return  The name of the task state that should be used if a timeout is
935   *          encountered, or {@code null} if the server should determine the
936   *          appropriate task state.
937   */
938  @Nullable()
939  public String getTaskStateIfTimeoutIsEncountered()
940  {
941    return taskStateIfTimeoutIsEncountered;
942  }
943
944
945
946  /**
947   * {@inheritDoc}
948   */
949  @Override()
950  @NotNull()
951  protected List<String> getAdditionalObjectClasses()
952  {
953    return Collections.singletonList(OC_DELAY_TASK);
954  }
955
956
957
958  /**
959   * {@inheritDoc}
960   */
961  @Override()
962  @NotNull()
963  protected List<Attribute> getAdditionalAttributes()
964  {
965    final LinkedList<Attribute> attrList = new LinkedList<>();
966
967    if (sleepDurationMillis != null)
968    {
969      final long sleepDurationNanos = sleepDurationMillis * 1_000_000L;
970      attrList.add(new Attribute(ATTR_SLEEP_DURATION,
971           DurationArgument.nanosToDuration(sleepDurationNanos)));
972    }
973
974    if (millisToWaitForWorkQueueToBecomeIdle != null)
975    {
976      final long waitTimeNanos =
977           millisToWaitForWorkQueueToBecomeIdle * 1_000_000L;
978      attrList.add(new Attribute(ATTR_WAIT_FOR_WORK_QUEUE_IDLE,
979           DurationArgument.nanosToDuration(waitTimeNanos)));
980    }
981
982    if (! ldapURLsForSearchesExpectedToReturnEntries.isEmpty())
983    {
984      final ArrayList<String> urlStrings =
985           new ArrayList<>(ldapURLsForSearchesExpectedToReturnEntries.size());
986      for (final LDAPURL url : ldapURLsForSearchesExpectedToReturnEntries)
987      {
988        urlStrings.add(url.toString());
989      }
990
991      attrList.add(new Attribute(ATTR_SEARCH_URL, urlStrings));
992    }
993
994    if (millisBetweenSearches != null)
995    {
996      final long intervalNanos = millisBetweenSearches * 1_000_000L;
997      attrList.add(new Attribute(ATTR_SEARCH_INTERVAL,
998           DurationArgument.nanosToDuration(intervalNanos)));
999    }
1000
1001    if (searchTimeLimitMillis != null)
1002    {
1003      final long timeLimitNanos = searchTimeLimitMillis * 1_000_000L;
1004      attrList.add(new Attribute(ATTR_SEARCH_TIME_LIMIT,
1005           DurationArgument.nanosToDuration(timeLimitNanos)));
1006    }
1007
1008    if (totalDurationMillisForEachLDAPURL != null)
1009    {
1010      final long durationNanos = totalDurationMillisForEachLDAPURL * 1_000_000L;
1011      attrList.add(new Attribute(ATTR_SEARCH_DURATION,
1012           DurationArgument.nanosToDuration(durationNanos)));
1013    }
1014
1015    if (taskStateIfTimeoutIsEncountered != null)
1016    {
1017      attrList.add(new Attribute(ATTR_TIMEOUT_RETURN_STATE,
1018           taskStateIfTimeoutIsEncountered));
1019    }
1020
1021    return attrList;
1022  }
1023
1024
1025
1026  /**
1027   * {@inheritDoc}
1028   */
1029  @Override()
1030  @NotNull()
1031  public List<TaskProperty> getTaskSpecificProperties()
1032  {
1033    return Collections.unmodifiableList(Arrays.asList(
1034         PROPERTY_SLEEP_DURATION_MILLIS,
1035         PROPERTY_WAIT_FOR_WORK_QUEUE_IDLE_MILLIS,
1036         PROPERTY_SEARCH_URL,
1037         PROPERTY_SEARCH_INTERVAL_MILLIS,
1038         PROPERTY_SEARCH_TIME_LIMIT_MILLIS,
1039         PROPERTY_SEARCH_DURATION_MILLIS,
1040         PROPERTY_TIMEOUT_RETURN_STATE));
1041  }
1042
1043
1044
1045  /**
1046   * {@inheritDoc}
1047   */
1048  @Override()
1049  @NotNull()
1050  public Map<TaskProperty,List<Object>> getTaskPropertyValues()
1051  {
1052    final LinkedHashMap<TaskProperty, List<Object>> props =
1053         new LinkedHashMap<>(StaticUtils.computeMapCapacity(7));
1054
1055    if (sleepDurationMillis != null)
1056    {
1057      props.put(PROPERTY_SLEEP_DURATION_MILLIS,
1058           Collections.<Object>singletonList(sleepDurationMillis));
1059    }
1060
1061    if (millisToWaitForWorkQueueToBecomeIdle != null)
1062    {
1063      props.put(PROPERTY_WAIT_FOR_WORK_QUEUE_IDLE_MILLIS,
1064           Collections.<Object>singletonList(
1065                millisToWaitForWorkQueueToBecomeIdle));
1066    }
1067
1068    if (! ldapURLsForSearchesExpectedToReturnEntries.isEmpty())
1069    {
1070      final List<String> urlStrings =
1071           new ArrayList<>(ldapURLsForSearchesExpectedToReturnEntries.size());
1072      for (final LDAPURL url : ldapURLsForSearchesExpectedToReturnEntries)
1073      {
1074        urlStrings.add(url.toString());
1075      }
1076      props.put(PROPERTY_SEARCH_URL,
1077
1078           Collections.<Object>unmodifiableList(urlStrings));
1079    }
1080
1081    if (millisBetweenSearches != null)
1082    {
1083      props.put(PROPERTY_SEARCH_INTERVAL_MILLIS,
1084           Collections.<Object>singletonList(millisBetweenSearches));
1085    }
1086
1087    if (searchTimeLimitMillis != null)
1088    {
1089      props.put(PROPERTY_SEARCH_TIME_LIMIT_MILLIS,
1090           Collections.<Object>singletonList(searchTimeLimitMillis));
1091    }
1092
1093    if (totalDurationMillisForEachLDAPURL != null)
1094    {
1095      props.put(PROPERTY_SEARCH_DURATION_MILLIS,
1096           Collections.<Object>singletonList(
1097                totalDurationMillisForEachLDAPURL));
1098    }
1099
1100    if (taskStateIfTimeoutIsEncountered != null)
1101    {
1102      props.put(PROPERTY_TIMEOUT_RETURN_STATE,
1103           Collections.<Object>singletonList(taskStateIfTimeoutIsEncountered));
1104    }
1105
1106    return Collections.unmodifiableMap(props);
1107  }
1108}