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