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