001    /*
002     * Copyright 2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2015 UnboundID Corp.
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     */
021    package com.unboundid.ldap.sdk.unboundidds.tasks;
022    
023    
024    
025    import java.util.ArrayList;
026    import java.util.Arrays;
027    import java.util.Collections;
028    import java.util.Date;
029    import java.util.LinkedHashMap;
030    import java.util.List;
031    import java.util.Map;
032    
033    import com.unboundid.ldap.sdk.Attribute;
034    import com.unboundid.ldap.sdk.Entry;
035    import com.unboundid.util.NotMutable;
036    import com.unboundid.util.ThreadSafety;
037    import com.unboundid.util.ThreadSafetyLevel;
038    
039    import static com.unboundid.ldap.sdk.unboundidds.tasks.TaskMessages.*;
040    import static com.unboundid.util.Validator.*;
041    
042    
043    
044    /**
045     * <BLOCKQUOTE>
046     *   <B>NOTE:</B>  This class is part of the Commercial Edition of the UnboundID
047     *   LDAP SDK for Java.  It is not available for use in applications that
048     *   include only the Standard Edition of the LDAP SDK, and is not supported for
049     *   use in conjunction with non-UnboundID products.
050     * </BLOCKQUOTE>
051     * This class defines a Directory Proxy Server task that can be used to reload
052     * the contents of the global index.  The properties that are available for use
053     * with this type of task include:
054     * <UL>
055     *   <LI>The base DN for the entry-balancing request processor.</LI>
056     *   <LI>An optional set of attributes for which to reload the index
057     *       information.</LI>
058     *   <LI>A flag indicating whether to perform the reload in the background.</LI>
059     *   <LI>A flag indicating whether to reload entries from backend Directory
060     *       Server instances rather than a peer Directory Proxy Server
061     *       instance.</LI>
062     *   <LI>An optional maximum number of entries per second to access when
063     *       priming.</LI>
064     * </UL>
065     */
066    @NotMutable()
067    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
068    public final class ReloadGlobalIndexTask
069           extends Task
070    {
071      /**
072       * The fully-qualified name of the Java class that is used for the re-encode
073       * entries task.
074       */
075      static final String RELOAD_GLOBAL_INDEX_TASK_CLASS =
076           "com.unboundid.directory.proxy.tasks.ReloadTask";
077    
078    
079    
080      /**
081       * The name of the attribute used to indicate whether the reload should be
082       * done in the background.
083       */
084      private static final String ATTR_BACKGROUND_RELOAD =
085           "ds-task-reload-background";
086    
087    
088    
089      /**
090       * The name of the attribute used to specify the base DN for the
091       * entry-balancing request processor.
092       */
093      private static final String ATTR_BASE_DN = "ds-task-reload-base-dn";
094    
095    
096    
097      /**
098       * The name of the attribute used to specify the names of the attributes for
099       * which to reload the indexes.
100       */
101      private static final String ATTR_INDEX_NAME = "ds-task-reload-index-name";
102    
103    
104    
105      /**
106       * The name of the attribute used to specify a target rate limit for the
107       * maximum number of entries per second.
108       */
109      private static final String ATTR_MAX_ENTRIES_PER_SECOND =
110           "ds-task-search-entry-per-second";
111    
112    
113    
114      /**
115       * The name of the attribute used to indicate whether the data should be
116       * loaded from backend Directory Server instances rather than a peer Directory
117       * Proxy Server instance.
118       */
119      private static final String ATTR_RELOAD_FROM_DS = "ds-task-reload-from-ds";
120    
121    
122    
123      /**
124       * The name of the object class used in reload global index task entries.
125       */
126      private static final String OC_RELOAD_GLOBAL_INDEX_TASK =
127           "ds-task-reload-index";
128    
129    
130    
131      /**
132       * The task property that will be used for the request processor base DN.
133       */
134      static final TaskProperty PROPERTY_BACKGROUND_RELOAD = new TaskProperty(
135           ATTR_BACKGROUND_RELOAD,
136           INFO_DISPLAY_NAME_RELOAD_GLOBAL_INDEX_BACKGROUND.get(),
137           INFO_DESCRIPTION_RELOAD_GLOBAL_INDEX_BACKGROUND.get(), Boolean.class,
138           false, false, false);
139    
140    
141    
142      /**
143       * The task property that will be used for the request processor base DN.
144       */
145      static final TaskProperty PROPERTY_BASE_DN = new TaskProperty(
146           ATTR_BASE_DN, INFO_DISPLAY_NAME_RELOAD_GLOBAL_INDEX_BASE_DN.get(),
147           INFO_DESCRIPTION_RELOAD_GLOBAL_INDEX_BASE_DN.get(), String.class, true,
148           false, false);
149    
150    
151    
152      /**
153       * The task property that will be used for the request processor base DN.
154       */
155      static final TaskProperty PROPERTY_INDEX_NAME = new TaskProperty(
156           ATTR_INDEX_NAME, INFO_DISPLAY_NAME_RELOAD_GLOBAL_INDEX_ATTR_NAME.get(),
157           INFO_DESCRIPTION_RELOAD_GLOBAL_INDEX_ATTR_NAME.get(), String.class,
158           false, true, false);
159    
160    
161    
162      /**
163       * The task property that will be used for the request processor base DN.
164       */
165      static final TaskProperty PROPERTY_MAX_ENTRIES_PER_SECOND = new TaskProperty(
166           ATTR_MAX_ENTRIES_PER_SECOND,
167           INFO_DISPLAY_NAME_RELOAD_GLOBAL_INDEX_MAX_ENTRIES_PER_SECOND.get(),
168           INFO_DESCRIPTION_RELOAD_GLOBAL_INDEX_MAX_ENTRIES_PER_SECOND.get(),
169           Long.class, false, false, false);
170    
171    
172    
173      /**
174       * The task property that will be used for the request processor base DN.
175       */
176      static final TaskProperty PROPERTY_RELOAD_FROM_DS = new TaskProperty(
177           ATTR_RELOAD_FROM_DS,
178           INFO_DISPLAY_NAME_RELOAD_GLOBAL_INDEX_RELOAD_FROM_DS.get(),
179           INFO_DESCRIPTION_RELOAD_GLOBAL_INDEX_RELOAD_FROM_DS.get(), Boolean.class,
180           false, false, false);
181    
182    
183    
184      /**
185       * The serial version UID for this serializable class.
186       */
187      private static final long serialVersionUID = 9152807987055252560L;
188    
189    
190    
191      // Indicates whether to reload from backend Directory Server instances.
192      private final Boolean reloadFromDS;
193    
194      // Indicates whether to reload in the background.
195      private final Boolean reloadInBackground;
196    
197      // The names of the indexes to reload.
198      private final List<String> indexNames;
199    
200      // The target maximum rate limit to use when loading entry data.
201      private final Long maxEntriesPerSecond;
202    
203      // The base DN for the entry-balancing request processor.
204      private final String baseDN;
205    
206    
207    
208      /**
209       * Creates a new uninitialized reload global index task instance which should
210       * only be used for obtaining general information about this task, including
211       * the task name, description, and supported properties.  Attempts to use a
212       * task created with this constructor for any other reason will likely fail.
213       */
214      public ReloadGlobalIndexTask()
215      {
216        reloadFromDS        = null;
217        reloadInBackground  = null;
218        indexNames          = null;
219        maxEntriesPerSecond = null;
220        baseDN              = null;
221      }
222    
223    
224    
225    
226      /**
227       * Creates a new reload global index task with the provided information.
228       *
229       * @param  taskID               The task ID to use for this task.  If it is
230       *                              {@code null} then a UUID will be generated for
231       *                              use as the task ID.
232       * @param  baseDN               The base DN of the entry-balancing request
233       *                              processor for which to reload index
234       *                              information.
235       * @param  indexNames           The names of the attributes for which to
236       *                              reload index data.  This may be {@code null}
237       *                              or empty to indicate that all indexes should
238       *                              be reloaded.
239       * @param  reloadFromDS         Indicates whether to load index data from
240       *                              backend Directory Server instances rather than
241       *                              a peer Directory Proxy Server instance.  This
242       *                              may be {@code null} to indicate that the
243       *                              Directory Proxy Server should automatically
244       *                              select the appropriate source for obtaining
245       *                              index data.
246       * @param  reloadInBackground   Indicates whether to perform the reload in
247       *                              the background, so that the task completes
248       *                              immediately.
249       * @param  maxEntriesPerSecond  The maximum target rate at which to reload
250       *                              index data (in entries per second).  A value
251       *                              of zero indicates no limit.  A value of
252       *                              {@code null} indicates that the Directory
253       *                              Proxy Server should attempt to determine the
254       *                              limit based on its configuration.
255       */
256      public ReloadGlobalIndexTask(final String taskID, final String baseDN,
257                                   final List<String> indexNames,
258                                   final Boolean reloadFromDS,
259                                   final Boolean reloadInBackground,
260                                   final Long maxEntriesPerSecond)
261      {
262        this(taskID, baseDN, indexNames, reloadFromDS, reloadInBackground,
263             maxEntriesPerSecond, null, null, null, null, null);
264      }
265    
266    
267    
268      /**
269       * Creates a new reload global index 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 of the entry-balancing request
275       *                                 processor for which to reload index
276       *                                 information.
277       * @param  indexNames              The names of the attributes for which to
278       *                                 reload index data.  This may be
279       *                                 {@code null} or empty to indicate that all
280       *                                 indexes should be reloaded.
281       * @param  reloadFromDS            Indicates whether to load index data from
282       *                                 backend Directory Server instances rather
283       *                                 than a peer Directory Proxy Server
284       *                                 instance.  This may be {@code null} to
285       *                                 indicate that the Directory Proxy Server
286       *                                 should automatically select the appropriate
287       *                                 source for obtaining index data.
288       * @param  reloadInBackground      Indicates whether to perform the reload in
289       *                                 the background, so that the task completes
290       *                                 immediately.
291       * @param  maxEntriesPerSecond     The maximum target rate at which to reload
292       *                                 index data (in entries per second).  A
293       *                                 value of zero indicates no limit.  A value
294       *                                 of {@code null} indicates that the
295       *                                 Directory Proxy Server should attempt to
296       *                                 determine the limit based on its
297       *                                 configuration.
298       * @param  scheduledStartTime      The time that this task should start
299       *                                 running.
300       * @param  dependencyIDs           The list of task IDs that will be required
301       *                                 to complete before this task will be
302       *                                 eligible to start.
303       * @param  failedDependencyAction  Indicates what action should be taken if
304       *                                 any of the dependencies for this task do
305       *                                 not complete successfully.
306       * @param  notifyOnCompletion      The list of e-mail addresses of individuals
307       *                                 that should be notified when this task
308       *                                 completes.
309       * @param  notifyOnError           The list of e-mail addresses of individuals
310       *                                 that should be notified if this task does
311       *                                 not complete successfully.
312       */
313      public ReloadGlobalIndexTask(final String taskID, final String baseDN,
314                  final List<String> indexNames, final Boolean reloadFromDS,
315                  final Boolean reloadInBackground, final Long maxEntriesPerSecond,
316                  final Date scheduledStartTime,
317                  final List<String> dependencyIDs,
318                  final FailedDependencyAction failedDependencyAction,
319                  final List<String> notifyOnCompletion,
320                  final List<String> notifyOnError)
321      {
322        super(taskID, RELOAD_GLOBAL_INDEX_TASK_CLASS, scheduledStartTime,
323             dependencyIDs, failedDependencyAction, notifyOnCompletion,
324             notifyOnError);
325    
326        ensureNotNull(baseDN);
327    
328        this.baseDN              = baseDN;
329        this.reloadFromDS        = reloadFromDS;
330        this.reloadInBackground  = reloadInBackground;
331        this.maxEntriesPerSecond = maxEntriesPerSecond;
332    
333        if (indexNames == null)
334        {
335          this.indexNames = Collections.emptyList();
336        }
337        else
338        {
339          this.indexNames = Collections.unmodifiableList(
340               new ArrayList<String>(indexNames));
341        }
342      }
343    
344    
345    
346      /**
347       * Creates a new reload global index task from the provided entry.
348       *
349       * @param  entry  The entry to use to create this reload global index task.
350       *
351       * @throws  TaskException  If the provided entry cannot be parsed as a reload
352       *                         global index task entry.
353       */
354      public ReloadGlobalIndexTask(final Entry entry)
355             throws TaskException
356      {
357        super(entry);
358    
359        // Get the base DN.  It must be present.
360        baseDN = entry.getAttributeValue(ATTR_BASE_DN);
361        if (baseDN == null)
362        {
363          throw new TaskException(
364               ERR_RELOAD_GLOBAL_INDEX_MISSING_REQUIRED_ATTR.get(ATTR_BASE_DN));
365        }
366    
367        // Get the names of the indexes to reload.  It may be empty or null.
368        final String[] nameArray = entry.getAttributeValues(ATTR_INDEX_NAME);
369        if ((nameArray == null) || (nameArray.length == 0))
370        {
371          indexNames = Collections.emptyList();
372        }
373        else
374        {
375          indexNames = Collections.unmodifiableList(Arrays.asList(nameArray));
376        }
377    
378        // Get the flag indicating whether to reload from backend Directory Server
379        // instances.
380        reloadFromDS = entry.getAttributeValueAsBoolean(ATTR_RELOAD_FROM_DS);
381    
382        // Get the flag indicating whether to reload in a background thread.
383        reloadInBackground =
384             entry.getAttributeValueAsBoolean(ATTR_BACKGROUND_RELOAD);
385    
386        // Get the value specifying the maximum reload rate in entries per second.
387        maxEntriesPerSecond =
388             entry.getAttributeValueAsLong(ATTR_MAX_ENTRIES_PER_SECOND);
389      }
390    
391    
392    
393      /**
394       * Creates a new reload global index task from the provided set of task
395       * properties.
396       *
397       * @param  properties  The set of task properties and their corresponding
398       *                     values to use for the task.  It must not be
399       *                     {@code null}.
400       *
401       * @throws  TaskException  If the provided set of properties cannot be used to
402       *                         create a valid reload global index task.
403       */
404      public ReloadGlobalIndexTask(final Map<TaskProperty,List<Object>> properties)
405             throws TaskException
406      {
407        super(RELOAD_GLOBAL_INDEX_TASK_CLASS, properties);
408    
409        final List<String> attrs = new ArrayList<String>(10);
410        Boolean background   = null;
411        Boolean fromDS       = null;
412        Long    maxPerSecond = null;
413        String  baseDNStr    = null;
414    
415        for (final Map.Entry<TaskProperty,List<Object>> e : properties.entrySet())
416        {
417          final TaskProperty p = e.getKey();
418          final String attrName = p.getAttributeName();
419          final List<Object> values = e.getValue();
420    
421          if (attrName.equalsIgnoreCase(ATTR_BASE_DN))
422          {
423            baseDNStr = parseString(p, values, null);
424          }
425          else if (attrName.equalsIgnoreCase(ATTR_INDEX_NAME))
426          {
427            final String[] nameArray = parseStrings(p, values, null);
428            if (nameArray != null)
429            {
430              attrs.addAll(Arrays.asList(nameArray));
431            }
432          }
433          else if (attrName.equalsIgnoreCase(ATTR_RELOAD_FROM_DS))
434          {
435            fromDS = parseBoolean(p, values, null);
436          }
437          else if (attrName.equalsIgnoreCase(ATTR_BACKGROUND_RELOAD))
438          {
439            background = parseBoolean(p, values, null);
440          }
441          else if (attrName.equalsIgnoreCase(ATTR_MAX_ENTRIES_PER_SECOND))
442          {
443            maxPerSecond = parseLong(p, values, null);
444          }
445        }
446    
447        if (baseDNStr == null)
448        {
449          throw new TaskException(
450               ERR_RELOAD_GLOBAL_INDEX_MISSING_REQUIRED_PROPERTY.get(ATTR_BASE_DN));
451        }
452    
453        baseDN              = baseDNStr;
454        indexNames          = Collections.unmodifiableList(attrs);
455        reloadFromDS        = fromDS;
456        reloadInBackground  = background;
457        maxEntriesPerSecond = maxPerSecond;
458      }
459    
460    
461    
462      /**
463       * {@inheritDoc}
464       */
465      @Override()
466      public String getTaskName()
467      {
468        return INFO_TASK_NAME_RELOAD_GLOBAL_INDEX.get();
469      }
470    
471    
472    
473      /**
474       * {@inheritDoc}
475       */
476      @Override()
477      public String getTaskDescription()
478      {
479        return INFO_TASK_DESCRIPTION_RELOAD_GLOBAL_INDEX.get();
480      }
481    
482    
483    
484      /**
485       * Retrieves the base DN of the entry-balancing request processor for which to
486       * reload index data.
487       *
488       * @return  The base DN of the entry-balancing request processor for which to
489       *          reload index data.
490       */
491      public String getBaseDN()
492      {
493        return baseDN;
494      }
495    
496    
497    
498      /**
499       * Retrieves the names of the indexes to be reloaded.
500       *
501       * @return  The names of the indexes to be reloaded, or an empty list if the
502       *          Directory Proxy Server should reload all indexes.
503       */
504      public List<String> getIndexNames()
505      {
506        return indexNames;
507      }
508    
509    
510    
511      /**
512       * Indicates whether to reload index information from backend Directory
513       * Servers rather than a peer Directory Proxy Server.
514       *
515       * @return  {@code true} if the index information should be reloaded from
516       *          backend Directory Servers, {@code false} if the index information
517       *          should be reloaded from a peer Directory Proxy Server instance, or
518       *          {@code null} if the Directory Proxy Server should automatically
519       *          determine the reload data source.
520       */
521      public Boolean reloadFromDS()
522      {
523        return reloadFromDS;
524      }
525    
526    
527    
528      /**
529       * Indicates whether to perform the index reload processing in the background.
530       *
531       * @return  {@code true} if the index reload processing should be performed
532       *          in the background (so that the task completes immediately),
533       *          {@code false} if not, or {@code null} if the Directory Proxy
534       *          Server should determine whether to perform the reload in the
535       *          background.
536       */
537      public Boolean reloadInBackground()
538      {
539        return reloadInBackground;
540      }
541    
542    
543    
544      /**
545       * Retrieves the maximum reload rate in entries per second, if defined.
546       *
547       * @return  The maximum rate at which to reload index data, in entries per
548       *          second, zero if no limit should be imposed, or {@code null} if the
549       *          Directory Proxy Server should determine the maximum reload rate.
550       */
551      public Long getMaxEntriesPerSecond()
552      {
553        return maxEntriesPerSecond;
554      }
555    
556    
557    
558      /**
559       * {@inheritDoc}
560       */
561      @Override()
562      protected List<String> getAdditionalObjectClasses()
563      {
564        return Arrays.asList(OC_RELOAD_GLOBAL_INDEX_TASK);
565      }
566    
567    
568    
569      /**
570       * {@inheritDoc}
571       */
572      @Override()
573      protected List<Attribute> getAdditionalAttributes()
574      {
575        final ArrayList<Attribute> attrList = new ArrayList<Attribute>(5);
576    
577        attrList.add(new Attribute(ATTR_BASE_DN, baseDN));
578    
579        if (! indexNames.isEmpty())
580        {
581          attrList.add(new Attribute(ATTR_INDEX_NAME, indexNames));
582        }
583    
584        if (reloadFromDS != null)
585        {
586          attrList.add(new Attribute(ATTR_RELOAD_FROM_DS,
587               String.valueOf(reloadFromDS)));
588        }
589    
590        if (reloadInBackground != null)
591        {
592          attrList.add(new Attribute(ATTR_BACKGROUND_RELOAD,
593               String.valueOf(reloadInBackground)));
594        }
595    
596        if (maxEntriesPerSecond != null)
597        {
598          attrList.add(new Attribute(ATTR_MAX_ENTRIES_PER_SECOND,
599               String.valueOf(maxEntriesPerSecond)));
600        }
601    
602        return attrList;
603      }
604    
605    
606    
607      /**
608       * {@inheritDoc}
609       */
610      @Override()
611      public List<TaskProperty> getTaskSpecificProperties()
612      {
613        return Collections.unmodifiableList(Arrays.asList(
614             PROPERTY_BASE_DN,
615             PROPERTY_INDEX_NAME,
616             PROPERTY_RELOAD_FROM_DS,
617             PROPERTY_BACKGROUND_RELOAD,
618             PROPERTY_MAX_ENTRIES_PER_SECOND));
619      }
620    
621    
622    
623      /**
624       * {@inheritDoc}
625       */
626      @Override()
627      public Map<TaskProperty,List<Object>> getTaskPropertyValues()
628      {
629        final LinkedHashMap<TaskProperty,List<Object>> props =
630             new LinkedHashMap<TaskProperty,List<Object>>(15);
631    
632        props.put(PROPERTY_BASE_DN,
633             Collections.<Object>unmodifiableList(Arrays.asList(baseDN)));
634        props.put(PROPERTY_INDEX_NAME,
635             Collections.<Object>unmodifiableList(indexNames));
636    
637        if (reloadFromDS == null)
638        {
639          props.put(PROPERTY_RELOAD_FROM_DS,
640               Collections.emptyList());
641        }
642        else
643        {
644          props.put(PROPERTY_RELOAD_FROM_DS,
645               Collections.<Object>unmodifiableList(Arrays.asList(reloadFromDS)));
646        }
647    
648        if (reloadInBackground == null)
649        {
650          props.put(PROPERTY_BACKGROUND_RELOAD,
651               Collections.emptyList());
652        }
653        else
654        {
655          props.put(PROPERTY_BACKGROUND_RELOAD,
656               Collections.<Object>unmodifiableList(Arrays.asList(
657                    reloadInBackground)));
658        }
659    
660        if (maxEntriesPerSecond == null)
661        {
662          props.put(PROPERTY_MAX_ENTRIES_PER_SECOND,
663               Collections.emptyList());
664        }
665        else
666        {
667          props.put(PROPERTY_MAX_ENTRIES_PER_SECOND,
668               Collections.<Object>unmodifiableList(
669                    Arrays.asList(maxEntriesPerSecond)));
670        }
671    
672        props.putAll(super.getTaskPropertyValues());
673        return Collections.unmodifiableMap(props);
674      }
675    }