001/*
002 * Copyright 2008-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2008-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) 2008-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.Collections;
043import java.util.Date;
044import java.util.LinkedHashMap;
045import java.util.List;
046import java.util.Map;
047
048import com.unboundid.ldap.sdk.Attribute;
049import com.unboundid.ldap.sdk.Entry;
050import com.unboundid.util.Debug;
051import com.unboundid.util.NotMutable;
052import com.unboundid.util.NotNull;
053import com.unboundid.util.Nullable;
054import com.unboundid.util.StaticUtils;
055import com.unboundid.util.ThreadSafety;
056import com.unboundid.util.ThreadSafetyLevel;
057import com.unboundid.util.Validator;
058
059import static com.unboundid.ldap.sdk.unboundidds.tasks.TaskMessages.*;
060
061
062
063/**
064 * This class defines a Directory Server task that can be used to generate
065 * and/or rebuild one or more indexes a Berkeley DB Java Edition backend.
066 * <BR>
067 * <BLOCKQUOTE>
068 *   <B>NOTE:</B>  This class, and other classes within the
069 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
070 *   supported for use against Ping Identity, UnboundID, and
071 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
072 *   for proprietary functionality or for external specifications that are not
073 *   considered stable or mature enough to be guaranteed to work in an
074 *   interoperable way with other types of LDAP servers.
075 * </BLOCKQUOTE>
076 * <BR>
077 * The properties that are available for use with this type of task include:
078 * <UL>
079 *   <LI>The backend base DN for which to perform the index rebuild.  This
080 *       must be provided when scheduling a rebuild task.</LI>
081 *   <LI>The names of the indexes to be built.  At least one index name must be
082 *       provided when scheduling a rebuild task.</LI>
083 *   <LI>The maximum number of concurrent threads that should be used to perform
084 *       the processing.  A value of zero indicates that there is no limit.</LI>
085 * </UL>
086
087 */
088@NotMutable()
089@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
090public final class RebuildTask
091       extends Task
092{
093  /**
094   * The fully-qualified name of the Java class that is used for the rebuild
095   * task.
096   */
097  @NotNull static final String REBUILD_TASK_CLASS =
098       "com.unboundid.directory.server.tasks.RebuildTask";
099
100
101
102  /**
103   * The name of the attribute used to specify the base DN for which to rebuild
104   * the specified indexes.
105   */
106  @NotNull private static final String ATTR_BASE_DN = "ds-task-rebuild-base-dn";
107
108
109
110  /**
111   * The name of the attribute used to specify the names of the indexes to
112   * rebuild.
113   */
114  @NotNull private static final String ATTR_INDEX = "ds-task-rebuild-index";
115
116
117
118  /**
119   * The name of the attribute used to specify the maximum number of concurrent
120   * threads to use to perform the rebuild.
121   */
122  @NotNull private static final String ATTR_MAX_THREADS =
123       "ds-task-rebuild-max-threads";
124
125
126
127  /**
128   * The name of the object class used in rebuild task entries.
129   */
130  @NotNull private static final String OC_REBUILD_TASK = "ds-task-rebuild";
131
132
133
134  /**
135   * The task property for the base DN.
136   */
137  @NotNull private static final TaskProperty PROPERTY_BASE_DN =
138       new TaskProperty(ATTR_BASE_DN, INFO_DISPLAY_NAME_BASE_DN_REBUILD.get(),
139                        INFO_DESCRIPTION_BASE_DN_REBUILD.get(), String.class,
140                        true, false, false);
141
142
143
144  /**
145   * The task property for the index names.
146   */
147  @NotNull private static final TaskProperty PROPERTY_INDEX =
148       new TaskProperty(ATTR_INDEX, INFO_DISPLAY_NAME_INDEX_REBUILD.get(),
149                        INFO_DESCRIPTION_INDEX_REBUILD.get(), String.class,
150                        true, true, false);
151
152
153
154  /**
155   * The task property for the max threads value.
156   */
157  @NotNull private static final TaskProperty PROPERTY_MAX_THREADS =
158       new TaskProperty(ATTR_MAX_THREADS,
159                        INFO_DISPLAY_NAME_MAX_THREADS_REBUILD.get(),
160                        INFO_DESCRIPTION_MAX_THREADS_REBUILD.get(), Long.class,
161                        false, false, true);
162
163
164
165  /**
166   * The serial version UID for this serializable class.
167   */
168  private static final long serialVersionUID = 6015907901926792443L;
169
170
171
172  // The maximum number of threads to use to rebuild indexes.
173  private final int maxThreads;
174
175  // The base DN for which to rebuild indexes.
176  @NotNull private final String baseDN;
177
178  // The names of the indexes to rebuild.
179  @NotNull private final List<String> indexes;
180
181
182
183  /**
184   * Creates a new uninitialized rebuild task instance which should only be used
185   * for obtaining general information about this task, including the task name,
186   * description, and supported properties.  Attempts to use a task created with
187   * this constructor for any other reason will likely fail.
188   */
189  public RebuildTask()
190  {
191    baseDN     = null;
192    maxThreads = -1;
193    indexes    = null;
194  }
195
196
197
198  /**
199   * Creates a new rebuild task with the provided information.
200   *
201   * @param  taskID   The task ID to use for this task.  If it is {@code null}
202   *                  then a UUID will be generated for use as the task ID.
203   * @param  baseDN   The base DN for which to rebuild the index.  It must refer
204   *                  to a base DN for a Berkeley DB Java Edition backend.  It
205   *                  must not be {@code null}.
206   * @param  indexes  A list containing the names of the indexes to rebuild.  It
207   *                  must not be {@code null} or empty.
208   */
209  public RebuildTask(@Nullable final String taskID,
210                     @NotNull final String baseDN,
211                     @NotNull final List<String> indexes)
212  {
213    this(taskID, baseDN, indexes, -1, null, null, null, null, null);
214  }
215
216
217
218  /**
219   * Creates a new rebuild task with the provided information.
220   *
221   * @param  taskID                  The task ID to use for this task.  If it is
222   *                                 {@code null} then a UUID will be generated
223   *                                 for use as the task ID.
224   * @param  baseDN                  The base DN for which to rebuild the index.
225   *                                 It must refer to a base DN for a Berkeley
226   *                                 DB Java Edition backend.  It must not be
227   *                                 {@code null}.
228   * @param  indexes                 A list containing the names of the indexes
229   *                                 to rebuild.  It must not be {@code null} or
230   *                                 empty.
231   * @param  maxThreads              The maximum number of concurrent threads to
232   *                                 use while performing the rebuild.  A value
233   *                                 less than or equal to zero indicates that
234   *                                 there is no limit to the number of threads
235   *                                 that may be used.
236   * @param  scheduledStartTime      The time that this task should start
237   *                                 running.
238   * @param  dependencyIDs           The list of task IDs that will be required
239   *                                 to complete before this task will be
240   *                                 eligible to start.
241   * @param  failedDependencyAction  Indicates what action should be taken if
242   *                                 any of the dependencies for this task do
243   *                                 not complete successfully.
244   * @param  notifyOnCompletion      The list of e-mail addresses of individuals
245   *                                 that should be notified when this task
246   *                                 completes.
247   * @param  notifyOnError           The list of e-mail addresses of individuals
248   *                                 that should be notified if this task does
249   *                                 not complete successfully.
250   */
251  public RebuildTask(@Nullable final String taskID,
252              @NotNull final String baseDN,
253              @NotNull final List<String> indexes,
254              final int maxThreads,
255              @Nullable final Date scheduledStartTime,
256              @Nullable final List<String> dependencyIDs,
257              @Nullable final FailedDependencyAction failedDependencyAction,
258              @Nullable final List<String> notifyOnCompletion,
259              @Nullable final List<String> notifyOnError)
260  {
261    this(taskID, baseDN, indexes, maxThreads, scheduledStartTime, dependencyIDs,
262         failedDependencyAction, null, notifyOnCompletion, null, notifyOnError,
263         null, null, null);
264  }
265
266
267
268  /**
269   * Creates a new rebuild task with the provided information.
270   *
271   * @param  taskID                  The task ID to use for this task.  If it is
272   *                                 {@code null} then a UUID will be generated
273   *                                 for use as the task ID.
274   * @param  baseDN                  The base DN for which to rebuild the index.
275   *                                 It must refer to a base DN for a Berkeley
276   *                                 DB Java Edition backend.  It must not be
277   *                                 {@code null}.
278   * @param  indexes                 A list containing the names of the indexes
279   *                                 to rebuild.  It must not be {@code null} or
280   *                                 empty.
281   * @param  maxThreads              The maximum number of concurrent threads to
282   *                                 use while performing the rebuild.  A value
283   *                                 less than or equal to zero indicates that
284   *                                 there is no limit to the number of threads
285   *                                 that may be used.
286   * @param  scheduledStartTime      The time that this task should start
287   *                                 running.
288   * @param  dependencyIDs           The list of task IDs that will be required
289   *                                 to complete before this task will be
290   *                                 eligible to start.
291   * @param  failedDependencyAction  Indicates what action should be taken if
292   *                                 any of the dependencies for this task do
293   *                                 not complete successfully.
294   * @param  notifyOnStart           The list of e-mail addresses of individuals
295   *                                 that should be notified when this task
296   *                                 starts running.
297   * @param  notifyOnCompletion      The list of e-mail addresses of individuals
298   *                                 that should be notified when this task
299   *                                 completes.
300   * @param  notifyOnSuccess         The list of e-mail addresses of individuals
301   *                                 that should be notified if this task
302   *                                 completes successfully.
303   * @param  notifyOnError           The list of e-mail addresses of individuals
304   *                                 that should be notified if this task does
305   *                                 not complete successfully.
306   * @param  alertOnStart            Indicates whether the server should send an
307   *                                 alert notification when this task starts.
308   * @param  alertOnSuccess          Indicates whether the server should send an
309   *                                 alert notification if this task completes
310   *                                 successfully.
311   * @param  alertOnError            Indicates whether the server should send an
312   *                                 alert notification if this task fails to
313   *                                 complete successfully.
314   */
315  public RebuildTask(@Nullable final String taskID,
316              @NotNull final String baseDN,
317              @NotNull final List<String> indexes,
318              final int maxThreads,
319              @Nullable final Date scheduledStartTime,
320              @Nullable final List<String> dependencyIDs,
321              @Nullable final FailedDependencyAction failedDependencyAction,
322              @Nullable final List<String> notifyOnStart,
323              @Nullable final List<String> notifyOnCompletion,
324              @Nullable final List<String> notifyOnSuccess,
325              @Nullable final List<String> notifyOnError,
326              @Nullable final Boolean alertOnStart,
327              @Nullable final Boolean alertOnSuccess,
328              @Nullable final Boolean alertOnError)
329  {
330    super(taskID, REBUILD_TASK_CLASS, scheduledStartTime, dependencyIDs,
331         failedDependencyAction, notifyOnStart, notifyOnCompletion,
332         notifyOnSuccess, notifyOnError, alertOnStart, alertOnSuccess,
333         alertOnError);
334
335    Validator.ensureNotNull(baseDN, indexes);
336    Validator.ensureFalse(indexes.isEmpty(),
337         "RebuildTask.indexes must not be empty.");
338
339    this.baseDN     = baseDN;
340    this.indexes    = Collections.unmodifiableList(indexes);
341    this.maxThreads = maxThreads;
342  }
343
344
345
346  /**
347   * Creates a new rebuild task from the provided entry.
348   *
349   * @param  entry  The entry to use to create this rebuild task.
350   *
351   * @throws  TaskException  If the provided entry cannot be parsed as a rebuild
352   *                         task entry.
353   */
354  public RebuildTask(@NotNull final Entry entry)
355         throws TaskException
356  {
357    super(entry);
358
359
360    // Get the base DN.  It must be present.
361    baseDN = entry.getAttributeValue(ATTR_BASE_DN);
362    if (baseDN == null)
363    {
364      throw new TaskException(ERR_REBUILD_TASK_NO_BASE_DN.get(
365                                   getTaskEntryDN()));
366    }
367
368
369    // Get the names of the indexes to rebuild.  It must be present.
370    final String[] indexArray = entry.getAttributeValues(ATTR_INDEX);
371    if ((indexArray == null) || (indexArray.length == 0))
372    {
373      throw new TaskException(ERR_REBUILD_TASK_NO_INDEXES.get(
374                                   getTaskEntryDN()));
375    }
376    else
377    {
378      indexes = Collections.unmodifiableList(Arrays.asList(indexArray));
379    }
380
381
382    // Get the maximum number of threads to use.
383    final String threadsStr = entry.getAttributeValue(ATTR_MAX_THREADS);
384    if (threadsStr == null)
385    {
386      maxThreads = -1;
387    }
388    else
389    {
390      try
391      {
392        maxThreads = Integer.parseInt(threadsStr);
393      }
394      catch (final Exception e)
395      {
396        Debug.debugException(e);
397        throw new TaskException(ERR_REBUILD_TASK_INVALID_MAX_THREADS.get(
398                                     getTaskEntryDN(), threadsStr), e);
399      }
400    }
401  }
402
403
404
405  /**
406   * Creates a new rebuild task from the provided set of task properties.
407   *
408   * @param  properties  The set of task properties and their corresponding
409   *                     values to use for the task.  It must not be
410   *                     {@code null}.
411   *
412   * @throws  TaskException  If the provided set of properties cannot be used to
413   *                         create a valid rebuild task.
414   */
415  public RebuildTask(@NotNull final Map<TaskProperty,List<Object>> properties)
416         throws TaskException
417  {
418    super(REBUILD_TASK_CLASS, properties);
419
420    long     t = -1;
421    String   b = null;
422    String[] i = null;
423
424    for (final Map.Entry<TaskProperty,List<Object>> entry :
425         properties.entrySet())
426    {
427      final TaskProperty p = entry.getKey();
428      final String attrName = p.getAttributeName();
429      final List<Object> values = entry.getValue();
430
431      if (attrName.equalsIgnoreCase(ATTR_BASE_DN))
432      {
433        b = parseString(p, values, b);
434      }
435      else if (attrName.equalsIgnoreCase(ATTR_INDEX))
436      {
437        i = parseStrings(p, values, i);
438      }
439      else if (attrName.equalsIgnoreCase(ATTR_MAX_THREADS))
440      {
441        t = parseLong(p, values, t);
442      }
443    }
444
445    if (b == null)
446    {
447      throw new TaskException(ERR_REBUILD_TASK_NO_BASE_DN.get(
448                                   getTaskEntryDN()));
449    }
450
451    if (i == null)
452    {
453      throw new TaskException(ERR_REBUILD_TASK_NO_INDEXES.get(
454                                   getTaskEntryDN()));
455    }
456
457    baseDN     = b;
458    indexes    = Collections.unmodifiableList(Arrays.asList(i));
459    maxThreads = (int) t;
460  }
461
462
463
464  /**
465   * {@inheritDoc}
466   */
467  @Override()
468  @NotNull()
469  public String getTaskName()
470  {
471    return INFO_TASK_NAME_REBUILD.get();
472  }
473
474
475
476  /**
477   * {@inheritDoc}
478   */
479  @Override()
480    @NotNull()
481public String getTaskDescription()
482  {
483    return INFO_TASK_DESCRIPTION_REBUILD.get();
484  }
485
486
487
488  /**
489   * Retrieves the base DN for which to rebuild the specified indexes.
490   *
491   * @return  The base DN for which to rebuild the specified indexes.
492   */
493  @NotNull()
494  public String getBaseDN()
495  {
496    return baseDN;
497  }
498
499
500
501  /**
502   * Retrieves the names of the indexes to be rebuilt.
503   *
504   * @return  The names of the indexes to be rebuilt.
505   */
506  @NotNull()
507  public List<String> getIndexNames()
508  {
509    return indexes;
510  }
511
512
513
514  /**
515   * Retrieves the maximum number of concurrent threads that should be used when
516   * rebuilding the indexes.
517   *
518   * @return  The maximum number of concurrent threads that should be used when
519   *          rebuilding the indexes, or a value less than or equal to zero if
520   *          there is no limit on the number of threads that may be used.
521   */
522  public int getMaxRebuildThreads()
523  {
524    return maxThreads;
525  }
526
527
528
529  /**
530   * {@inheritDoc}
531   */
532  @Override()
533  @NotNull()
534  protected List<String> getAdditionalObjectClasses()
535  {
536    return Collections.singletonList(OC_REBUILD_TASK);
537  }
538
539
540
541  /**
542   * {@inheritDoc}
543   */
544  @Override()
545  @NotNull()
546  protected List<Attribute> getAdditionalAttributes()
547  {
548    final ArrayList<Attribute> attrs = new ArrayList<>(3);
549
550    attrs.add(new Attribute(ATTR_BASE_DN, baseDN));
551    attrs.add(new Attribute(ATTR_INDEX, indexes));
552
553    if (maxThreads > 0)
554    {
555      attrs.add(new Attribute(ATTR_MAX_THREADS, String.valueOf(maxThreads)));
556    }
557
558    return attrs;
559  }
560
561
562
563  /**
564   * {@inheritDoc}
565   */
566  @Override()
567  @NotNull()
568  public List<TaskProperty> getTaskSpecificProperties()
569  {
570    final List<TaskProperty> propList = Arrays.asList(
571         PROPERTY_BASE_DN,
572         PROPERTY_INDEX,
573         PROPERTY_MAX_THREADS);
574
575    return Collections.unmodifiableList(propList);
576  }
577
578
579
580  /**
581   * {@inheritDoc}
582   */
583  @Override()
584  @NotNull()
585  public Map<TaskProperty,List<Object>> getTaskPropertyValues()
586  {
587    final LinkedHashMap<TaskProperty,List<Object>> props =
588         new LinkedHashMap<>(StaticUtils.computeMapCapacity(10));
589
590    props.put(PROPERTY_BASE_DN,
591              Collections.<Object>singletonList(baseDN));
592
593    props.put(PROPERTY_INDEX,
594              Collections.<Object>unmodifiableList(indexes));
595
596    props.put(PROPERTY_MAX_THREADS,
597              Collections.<Object>singletonList((long) maxThreads));
598
599    props.putAll(super.getTaskPropertyValues());
600    return Collections.unmodifiableMap(props);
601  }
602}