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