001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2008 Sun Microsystems, Inc.
015 * Portions Copyright 2014-2015 ForgeRock AS.
016 */
017package org.opends.server.admin;
018
019import java.util.EnumSet;
020
021import org.forgerock.opendj.config.DurationUnit;
022
023import static org.forgerock.util.Reject.*;
024
025/**
026 * Duration property definition.
027 * <p>
028 * A duration property definition comprises of:
029 * <ul>
030 * <li>a <i>base unit</i> - specifies the minimum granularity which
031 * can be used to specify duration property values. For example, if
032 * the base unit is in seconds then values represented in milliseconds
033 * will not be permitted. The default base unit is seconds
034 * <li>an optional <i>maximum unit</i> - specifies the biggest
035 * duration unit which can be used to specify duration property
036 * values. Values presented in units greater than this unit will not
037 * be permitted. There is no default maximum unit
038 * <li><i>lower limit</i> - specifies the smallest duration
039 * permitted by the property. The default lower limit is 0 and can
040 * never be less than 0
041 * <li>an optional <i>upper limit</i> - specifies the biggest
042 * duration permitted by the property. By default, there is no upper
043 * limit
044 * <li>support for <i>unlimited</i> durations - when permitted users
045 * can specify "unlimited" durations. These are represented using the
046 * decoded value, -1, or the encoded string value "unlimited". By
047 * default, unlimited durations are not permitted. In addition, it is
048 * not possible to define an upper limit and support unlimited values.
049 * </ul>
050 * Decoded values are represented using <code>long</code> values in
051 * the base unit defined for the duration property definition.
052 */
053public final class DurationPropertyDefinition extends PropertyDefinition<Long> {
054
055  /** String used to represent unlimited durations. */
056  private static final String UNLIMITED = "unlimited";
057
058  /** The base unit for this property definition. */
059  private final DurationUnit baseUnit;
060
061  /** The optional maximum unit for this property definition. */
062  private final DurationUnit maximumUnit;
063
064  /** The lower limit of the property value in milli-seconds. */
065  private final long lowerLimit;
066
067  /** The optional upper limit of the property value in milli-seconds. */
068  private final Long upperLimit;
069
070  /**
071   * Indicates whether this property allows the use of the "unlimited"
072   * duration value (represented using a -1L or the string
073   * "unlimited").
074   */
075  private final boolean allowUnlimited;
076
077
078
079  /**
080   * An interface for incrementally constructing duration property
081   * definitions.
082   */
083  public static class Builder extends
084      AbstractBuilder<Long, DurationPropertyDefinition> {
085
086    /** The base unit for this property definition. */
087    private DurationUnit baseUnit = DurationUnit.SECONDS;
088
089    /** The optional maximum unit for this property definition. */
090    private DurationUnit maximumUnit;
091
092    /** The lower limit of the property value in milli-seconds. */
093    private long lowerLimit;
094
095    /**
096     * The optional upper limit of the property value in
097     * milli-seconds.
098     */
099    private Long upperLimit;
100
101    /**
102     * Indicates whether this property allows the use of the
103     * "unlimited" duration value (represented using a -1L or the
104     * string "unlimited").
105     */
106    private boolean allowUnlimited;
107
108
109
110    /** Private constructor. */
111    private Builder(AbstractManagedObjectDefinition<?, ?> d,
112        String propertyName) {
113      super(d, propertyName);
114    }
115
116
117
118    /**
119     * Set the base unit for this property definition (values
120     * including limits are specified in this unit). By default a
121     * duration property definition uses seconds.
122     *
123     * @param unit
124     *          The string representation of the base unit (must not
125     *          be <code>null</code>).
126     * @throws IllegalArgumentException
127     *           If the provided unit name did not correspond to a
128     *           known duration unit, or if the base unit is bigger
129     *           than the maximum unit.
130     */
131    public final void setBaseUnit(String unit) throws IllegalArgumentException {
132      ifNull(unit);
133
134      setBaseUnit(DurationUnit.getUnit(unit));
135    }
136
137
138
139    /**
140     * Set the base unit for this property definition (values
141     * including limits are specified in this unit). By default a
142     * duration property definition uses seconds.
143     *
144     * @param unit
145     *          The base unit (must not be <code>null</code>).
146     * @throws IllegalArgumentException
147     *           If the provided base unit is bigger than the maximum
148     *           unit.
149     */
150    public final void setBaseUnit(DurationUnit unit)
151        throws IllegalArgumentException {
152      ifNull(unit);
153
154      // Make sure that the base unit is not bigger than the maximum unit.
155      if (maximumUnit != null && unit.getDuration() > maximumUnit.getDuration()) {
156        throw new IllegalArgumentException("Base unit greater than maximum unit");
157      }
158
159      this.baseUnit = unit;
160    }
161
162
163
164    /**
165     * Set the maximum unit for this property definition. By default
166     * there is no maximum unit.
167     *
168     * @param unit
169     *          The string representation of the maximum unit, or
170     *          <code>null</code> if there should not be a maximum
171     *          unit.
172     * @throws IllegalArgumentException
173     *           If the provided unit name did not correspond to a
174     *           known duration unit, or if the maximum unit is
175     *           smaller than the base unit.
176     */
177    public final void setMaximumUnit(String unit)
178        throws IllegalArgumentException {
179      if (unit == null) {
180        setMaximumUnit((DurationUnit) null);
181      } else {
182        setMaximumUnit(DurationUnit.getUnit(unit));
183      }
184    }
185
186
187
188    /**
189     * Set the maximum unit for this property definition. By default
190     * there is no maximum unit.
191     *
192     * @param unit
193     *          The maximum unit, or <code>null</code> if there
194     *          should not be a maximum unit.
195     * @throws IllegalArgumentException
196     *           If the provided maximum unit is smaller than the base unit.
197     */
198    public final void setMaximumUnit(DurationUnit unit)
199        throws IllegalArgumentException {
200      // Make sure that the maximum unit is not smaller than the base unit.
201      if (unit != null && unit.getDuration() < baseUnit.getDuration()) {
202        throw new IllegalArgumentException("Maximum unit smaller than base unit");
203      }
204
205      this.maximumUnit = unit;
206    }
207
208
209
210    /**
211     * Set the lower limit in milli-seconds.
212     *
213     * @param lowerLimit
214     *          The new lower limit (must be >= 0) in milli-seconds.
215     * @throws IllegalArgumentException
216     *           If a negative lower limit was specified, or the lower
217     *           limit is greater than the upper limit.
218     */
219    public final void setLowerLimit(long lowerLimit)
220        throws IllegalArgumentException {
221      if (lowerLimit < 0) {
222        throw new IllegalArgumentException("Negative lower limit");
223      }
224
225      if (upperLimit != null && lowerLimit > upperLimit) {
226        throw new IllegalArgumentException(
227            "Lower limit greater than upper limit");
228      }
229
230      this.lowerLimit = lowerLimit;
231    }
232
233
234
235    /**
236     * Set the lower limit using a string representation of the limit.
237     * If the string does not specify a unit, the current base unit
238     * will be used.
239     *
240     * @param lowerLimit
241     *          The string representation of the new lower limit.
242     * @throws IllegalArgumentException
243     *           If the lower limit could not be parsed, or if a
244     *           negative lower limit was specified, or the lower
245     *           limit is greater than the upper limit.
246     */
247    public final void setLowerLimit(String lowerLimit)
248        throws IllegalArgumentException {
249      setLowerLimit(DurationUnit.parseValue(lowerLimit, baseUnit));
250    }
251
252
253
254    /**
255     * Set the upper limit in milli-seconds.
256     *
257     * @param upperLimit
258     *          The new upper limit in milli-seconds, or
259     *          <code>null</code> if there is no upper limit.
260     * @throws IllegalArgumentException
261     *           If a negative upper limit was specified, or the lower
262     *           limit is greater than the upper limit or unlimited
263     *           durations are permitted.
264     */
265    public final void setUpperLimit(Long upperLimit)
266        throws IllegalArgumentException {
267      if (upperLimit != null) {
268        if (upperLimit < 0) {
269          throw new IllegalArgumentException("Negative upper limit");
270        }
271
272        if (lowerLimit > upperLimit) {
273          throw new IllegalArgumentException(
274              "Lower limit greater than upper limit");
275        }
276
277        if (allowUnlimited) {
278          throw new IllegalArgumentException(
279              "Upper limit specified when unlimited durations are permitted");
280        }
281      }
282
283      this.upperLimit = upperLimit;
284    }
285
286
287
288    /**
289     * Set the upper limit using a string representation of the limit.
290     * If the string does not specify a unit, the current base unit
291     * will be used.
292     *
293     * @param upperLimit
294     *          The string representation of the new upper limit, or
295     *          <code>null</code> if there is no upper limit.
296     * @throws IllegalArgumentException
297     *           If the upper limit could not be parsed, or if the
298     *           lower limit is greater than the upper limit.
299     */
300    public final void setUpperLimit(String upperLimit)
301        throws IllegalArgumentException {
302      if (upperLimit == null) {
303        setUpperLimit((Long) null);
304      } else {
305        setUpperLimit(DurationUnit.parseValue(upperLimit, baseUnit));
306      }
307    }
308
309
310
311    /**
312     * Specify whether or not this property definition will allow
313     * unlimited values (default is false).
314     *
315     * @param allowUnlimited
316     *          <code>true</code> if the property will allow
317     *          unlimited values, or <code>false</code> otherwise.
318     * @throws IllegalArgumentException
319     *           If unlimited values are to be permitted but there is
320     *           an upper limit specified.
321     */
322    public final void setAllowUnlimited(boolean allowUnlimited)
323        throws IllegalArgumentException {
324      if (allowUnlimited && upperLimit != null) {
325        throw new IllegalArgumentException(
326            "Upper limit specified when unlimited durations are permitted");
327      }
328
329      this.allowUnlimited = allowUnlimited;
330    }
331
332
333
334    /** {@inheritDoc} */
335    @Override
336    protected DurationPropertyDefinition buildInstance(
337        AbstractManagedObjectDefinition<?, ?> d, String propertyName,
338        EnumSet<PropertyOption> options,
339        AdministratorAction adminAction,
340        DefaultBehaviorProvider<Long> defaultBehavior) {
341      return new DurationPropertyDefinition(d, propertyName, options,
342          adminAction, defaultBehavior, baseUnit, maximumUnit, lowerLimit,
343          upperLimit, allowUnlimited);
344    }
345  }
346
347
348
349  /**
350   * Create a duration property definition builder.
351   *
352   * @param d
353   *          The managed object definition associated with this
354   *          property definition.
355   * @param propertyName
356   *          The property name.
357   * @return Returns the new integer property definition builder.
358   */
359  public static Builder createBuilder(AbstractManagedObjectDefinition<?, ?> d,
360      String propertyName) {
361    return new Builder(d, propertyName);
362  }
363
364
365
366  /** Private constructor. */
367  private DurationPropertyDefinition(AbstractManagedObjectDefinition<?, ?> d,
368      String propertyName, EnumSet<PropertyOption> options,
369      AdministratorAction adminAction,
370      DefaultBehaviorProvider<Long> defaultBehavior, DurationUnit baseUnit,
371      DurationUnit maximumUnit, Long lowerLimit, Long upperLimit,
372      boolean allowUnlimited) {
373    super(d, Long.class, propertyName, options, adminAction, defaultBehavior);
374    this.baseUnit = baseUnit;
375    this.maximumUnit = maximumUnit;
376    this.lowerLimit = lowerLimit;
377    this.upperLimit = upperLimit;
378    this.allowUnlimited = allowUnlimited;
379  }
380
381
382
383  /**
384   * Get the base unit for this property definition (values including
385   * limits are specified in this unit).
386   *
387   * @return Returns the base unit for this property definition
388   *         (values including limits are specified in this unit).
389   */
390  public DurationUnit getBaseUnit() {
391    return baseUnit;
392  }
393
394
395
396  /**
397   * Get the maximum unit for this property definition if specified.
398   *
399   * @return Returns the maximum unit for this property definition, or
400   *         <code>null</code> if there is no maximum unit.
401   */
402  public DurationUnit getMaximumUnit() {
403    return maximumUnit;
404  }
405
406
407
408  /**
409   * Get the lower limit in milli-seconds.
410   *
411   * @return Returns the lower limit in milli-seconds.
412   */
413  public long getLowerLimit() {
414    return lowerLimit;
415  }
416
417
418
419  /**
420   * Get the upper limit in milli-seconds.
421   *
422   * @return Returns the upper limit in milli-seconds, or
423   *         <code>null</code> if there is no upper limit.
424   */
425  public Long getUpperLimit() {
426    return upperLimit;
427  }
428
429
430
431  /**
432   * Determine whether this property allows unlimited durations.
433   *
434   * @return Returns <code>true</code> if this this property allows
435   *         unlimited durations.
436   */
437  public boolean isAllowUnlimited() {
438    return allowUnlimited;
439  }
440
441
442
443  /** {@inheritDoc} */
444  @Override
445  public void validateValue(Long value) throws PropertyException {
446    ifNull(value);
447
448    long nvalue = baseUnit.toMilliSeconds(value);
449    if (!allowUnlimited && nvalue < lowerLimit) {
450      throw PropertyException.illegalPropertyValueException(this, value);
451
452      // unlimited allowed
453    } else if (nvalue >= 0 && nvalue < lowerLimit) {
454      throw PropertyException.illegalPropertyValueException(this, value);
455    }
456
457    if (upperLimit != null && nvalue > upperLimit) {
458      throw PropertyException.illegalPropertyValueException(this, value);
459    }
460  }
461
462
463
464  /** {@inheritDoc} */
465  @Override
466  public String encodeValue(Long value) throws PropertyException {
467    ifNull(value);
468
469    // Make sure that we correctly encode negative values as "unlimited".
470    if (allowUnlimited && value < 0) {
471      return UNLIMITED;
472    }
473
474    // Encode the size value using the base unit.
475    return value + " " + baseUnit;
476  }
477
478
479
480  /** {@inheritDoc} */
481  @Override
482  public Long decodeValue(String value)
483      throws PropertyException {
484    ifNull(value);
485
486    // First check for the special "unlimited" value when necessary.
487    if (allowUnlimited && value.trim().equalsIgnoreCase(UNLIMITED)) {
488      return -1L;
489    }
490
491    // Parse the string representation.
492    long ms;
493    try {
494      ms = DurationUnit.parseValue(value);
495    } catch (NumberFormatException e) {
496      throw PropertyException.illegalPropertyValueException(this, value);
497    }
498
499    // Check the unit is in range - values must not be more granular
500    // than the base unit.
501    if (ms % baseUnit.getDuration() != 0) {
502      throw PropertyException.illegalPropertyValueException(this, value);
503    }
504
505    // Convert the value a long in the property's required unit.
506    Long i = (long) baseUnit.fromMilliSeconds(ms);
507    try {
508      validateValue(i);
509    } catch (PropertyException e) {
510      throw PropertyException.illegalPropertyValueException(this, value);
511    }
512    return i;
513  }
514
515
516
517  /** {@inheritDoc} */
518  @Override
519  public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) {
520    return v.visitDuration(this, p);
521  }
522
523
524
525  /** {@inheritDoc} */
526  @Override
527  public <R, P> R accept(PropertyValueVisitor<R, P> v, Long value, P p) {
528    return v.visitDuration(this, value, p);
529  }
530
531
532
533  /** {@inheritDoc} */
534  @Override
535  public void toString(StringBuilder builder) {
536    super.toString(builder);
537
538    builder.append(" baseUnit=");
539    builder.append(baseUnit);
540
541    if (maximumUnit != null) {
542      builder.append(" maximumUnit=");
543      builder.append(maximumUnit);
544    }
545
546    builder.append(" lowerLimit=");
547    builder.append(lowerLimit);
548    builder.append("ms");
549
550    if (upperLimit != null) {
551      builder.append(" upperLimit=");
552      builder.append(upperLimit);
553      builder.append("ms");
554    }
555
556    builder.append(" allowUnlimited=");
557    builder.append(allowUnlimited);
558  }
559
560
561
562  /** {@inheritDoc} */
563  @Override
564  public int compare(Long o1, Long o2) {
565    return o1.compareTo(o2);
566  }
567
568}