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