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