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.SizeUnit;
032
033import static org.forgerock.util.Reject.*;
034
035/**
036 * Memory size property definition.
037 * <p>
038 * All memory size property values are represented in bytes using longs.
039 * <p>
040 * All values must be zero or positive and within the lower/upper limit
041 * constraints. Support is provided for "unlimited" memory sizes. These are
042 * represented using a negative memory size value or using the string
043 * "unlimited".
044 */
045public final class SizePropertyDefinition extends PropertyDefinition<Long> {
046
047  /** String used to represent unlimited memory sizes. */
048  private static final String UNLIMITED = "unlimited";
049
050  /** The lower limit of the property value in bytes. */
051  private final long lowerLimit;
052
053  /** The optional upper limit of the property value in bytes. */
054  private final Long upperLimit;
055
056  /**
057   * Indicates whether this property allows the use of the "unlimited" memory
058   * size value (represented using a -1L or the string "unlimited").
059   */
060  private final boolean allowUnlimited;
061
062
063
064  /**
065   * An interface for incrementally constructing memory size property
066   * definitions.
067   */
068  public static class Builder extends
069      AbstractBuilder<Long, SizePropertyDefinition> {
070
071    /** The lower limit of the property value in bytes. */
072    private long lowerLimit;
073
074    /** The optional upper limit of the property value in bytes. */
075    private Long upperLimit;
076
077    /**
078     * Indicates whether this property allows the use of the "unlimited" memory
079     * size value (represented using a -1L or the string "unlimited").
080     */
081    private boolean allowUnlimited;
082
083
084
085    /** Private constructor. */
086    private Builder(
087        AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
088      super(d, propertyName);
089    }
090
091
092
093    /**
094     * Set the lower limit in bytes.
095     *
096     * @param lowerLimit
097     *          The new lower limit (must be >= 0) in bytes.
098     * @throws IllegalArgumentException
099     *           If a negative lower limit was specified, or if the lower limit
100     *           is greater than the upper limit.
101     */
102    public final void setLowerLimit(long lowerLimit)
103        throws IllegalArgumentException {
104      if (lowerLimit < 0) {
105        throw new IllegalArgumentException("Negative lower limit");
106      }
107      if (upperLimit != null && lowerLimit > upperLimit) {
108        throw new IllegalArgumentException(
109            "Lower limit greater than upper limit");
110      }
111      this.lowerLimit = lowerLimit;
112    }
113
114
115
116    /**
117     * Set the lower limit using a string representation of the limit.
118     *
119     * @param lowerLimit
120     *          The string representation of the new lower limit.
121     * @throws IllegalArgumentException
122     *           If the lower limit could not be parsed, or if a negative lower
123     *           limit was specified, or the lower limit is greater than the
124     *           upper limit.
125     */
126    public final void setLowerLimit(String lowerLimit)
127        throws IllegalArgumentException {
128      setLowerLimit(SizeUnit.parseValue(lowerLimit, SizeUnit.BYTES));
129    }
130
131
132
133    /**
134     * Set the upper limit in bytes.
135     *
136     * @param upperLimit
137     *          The new upper limit in bytes or <code>null</code> if there is
138     *          no upper limit.
139     * @throws IllegalArgumentException
140     *           If the lower limit is greater than the upper limit.
141     */
142    public final void setUpperLimit(Long upperLimit)
143        throws IllegalArgumentException {
144      if (upperLimit != null) {
145        if (upperLimit < 0) {
146          throw new IllegalArgumentException("Negative upper limit");
147        }
148        if (lowerLimit > upperLimit) {
149          throw new IllegalArgumentException(
150              "Lower limit greater than upper limit");
151        }
152      }
153      this.upperLimit = upperLimit;
154    }
155
156
157
158    /**
159     * Set the upper limit using a string representation of the limit.
160     *
161     * @param upperLimit
162     *          The string representation of the new upper limit, or
163     *          <code>null</code> if there is no upper limit.
164     * @throws IllegalArgumentException
165     *           If the upper limit could not be parsed, or if the lower limit
166     *           is greater than the upper limit.
167     */
168    public final void setUpperLimit(String upperLimit)
169        throws IllegalArgumentException {
170      if (upperLimit == null) {
171        setUpperLimit((Long) null);
172      } else {
173        setUpperLimit(SizeUnit.parseValue(upperLimit, SizeUnit.BYTES));
174      }
175    }
176
177
178
179    /**
180     * Specify whether or not this property definition will allow unlimited
181     * values (default is false).
182     *
183     * @param allowUnlimited
184     *          <code>true</code> if the property will allow unlimited values,
185     *          or <code>false</code> otherwise.
186     */
187    public final void setAllowUnlimited(boolean allowUnlimited) {
188      this.allowUnlimited = allowUnlimited;
189    }
190
191
192
193    /** {@inheritDoc} */
194    @Override
195    protected SizePropertyDefinition buildInstance(
196        AbstractManagedObjectDefinition<?, ?> d, String propertyName,
197        EnumSet<PropertyOption> options,
198        AdministratorAction adminAction,
199        DefaultBehaviorProvider<Long> defaultBehavior) {
200      return new SizePropertyDefinition(d, propertyName, options, adminAction,
201          defaultBehavior, lowerLimit, upperLimit, allowUnlimited);
202    }
203
204  }
205
206
207
208  /**
209   * Create an memory size property definition builder.
210   *
211   * @param d
212   *          The managed object definition associated with this
213   *          property definition.
214   * @param propertyName
215   *          The property name.
216   * @return Returns the new integer property definition builder.
217   */
218  public static Builder createBuilder(
219      AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
220    return new Builder(d, propertyName);
221  }
222
223
224
225  /** Private constructor. */
226  private SizePropertyDefinition(
227      AbstractManagedObjectDefinition<?, ?> d, String propertyName,
228      EnumSet<PropertyOption> options,
229      AdministratorAction adminAction,
230      DefaultBehaviorProvider<Long> defaultBehavior, Long lowerLimit,
231      Long upperLimit, boolean allowUnlimited) {
232    super(d, Long.class, propertyName, options, adminAction,
233        defaultBehavior);
234    this.lowerLimit = lowerLimit;
235    this.upperLimit = upperLimit;
236    this.allowUnlimited = allowUnlimited;
237  }
238
239
240
241  /**
242   * Get the lower limit in bytes.
243   *
244   * @return Returns the lower limit in bytes.
245   */
246  public long getLowerLimit() {
247    return lowerLimit;
248  }
249
250
251
252  /**
253   * Get the upper limit in bytes.
254   *
255   * @return Returns the upper limit in bytes or <code>null</code> if there is
256   *         no upper limit.
257   */
258  public Long getUpperLimit() {
259    return upperLimit;
260  }
261
262
263
264  /**
265   * Determine whether this property allows unlimited memory sizes.
266   *
267   * @return Returns <code>true</code> if this this property allows unlimited
268   *         memory sizes.
269   */
270  public boolean isAllowUnlimited() {
271    return allowUnlimited;
272  }
273
274
275
276  /** {@inheritDoc} */
277  @Override
278  public void validateValue(Long value) throws PropertyException {
279    ifNull(value);
280
281    if (!allowUnlimited && value < lowerLimit) {
282      throw PropertyException.illegalPropertyValueException(this, value);
283
284    // unlimited allowed
285    } else if (value >= 0 && value < lowerLimit) {
286      throw PropertyException.illegalPropertyValueException(this, value);
287    }
288
289    if (upperLimit != null && value > upperLimit) {
290      throw PropertyException.illegalPropertyValueException(this, value);
291    }
292  }
293
294
295
296  /** {@inheritDoc} */
297  @Override
298  public String encodeValue(Long value) throws PropertyException {
299    ifNull(value);
300
301    // Make sure that we correctly encode negative values as "unlimited".
302    if (allowUnlimited && value < 0) {
303      return UNLIMITED;
304    }
305
306    // Encode the size value using the best-fit unit.
307    SizeUnit unit = SizeUnit.getBestFitUnitExact(value);
308    long fromBytes = (long) unit.fromBytes(value);
309
310    // Cast to a long to remove fractional part (which should not be there
311    // anyway as the best-fit unit should result in an exact conversion).
312    return fromBytes + " " + unit;
313  }
314
315
316
317  /** {@inheritDoc} */
318  @Override
319  public Long decodeValue(String value) throws PropertyException {
320    ifNull(value);
321
322    // First check for the special "unlimited" value when necessary.
323    if (allowUnlimited && value.trim().equalsIgnoreCase(UNLIMITED)) {
324      return -1L;
325    }
326
327    // Decode the value.
328    Long i;
329    try {
330      i = SizeUnit.parseValue(value, SizeUnit.BYTES);
331    } catch (NumberFormatException e) {
332      throw PropertyException.illegalPropertyValueException(this, value);
333    }
334
335    try {
336      validateValue(i);
337    } catch (PropertyException e) {
338      throw PropertyException.illegalPropertyValueException(this, value);
339    }
340    return i;
341  }
342
343
344
345  /** {@inheritDoc} */
346  @Override
347  public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) {
348    return v.visitSize(this, p);
349  }
350
351
352
353  /** {@inheritDoc} */
354  @Override
355  public <R, P> R accept(PropertyValueVisitor<R, P> v, Long value, P p) {
356    return v.visitSize(this, value, p);
357  }
358
359
360
361  /** {@inheritDoc} */
362  @Override
363  public void toString(StringBuilder builder) {
364    super.toString(builder);
365
366    builder.append(" lowerLimit=");
367    builder.append(lowerLimit);
368
369    if (upperLimit != null) {
370      builder.append(" upperLimit=");
371      builder.append(upperLimit);
372    }
373
374    builder.append(" allowUnlimited=");
375    builder.append(allowUnlimited);
376
377  }
378
379
380
381  /** {@inheritDoc} */
382  @Override
383  public int compare(Long o1, Long o2) {
384    return o1.compareTo(o2);
385  }
386
387}