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