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