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 */ 017 018package org.forgerock.opendj.config; 019 020import org.forgerock.util.Reject; 021 022import java.util.EnumSet; 023 024/** 025 * Duration property definition. 026 * <p> 027 * A duration property definition comprises of: 028 * <ul> 029 * <li>a <i>base unit</i> - specifies the minimum granularity which can be used 030 * to specify duration property values. For example, if the base unit is in 031 * seconds then values represented in milliseconds will not be permitted. The 032 * default base unit is seconds 033 * <li>an optional <i>maximum unit</i> - specifies the biggest duration unit 034 * which can be used to specify duration property values. Values presented in 035 * units greater than this unit will not be permitted. There is no default 036 * maximum unit 037 * <li><i>lower limit</i> - specifies the smallest duration permitted by the 038 * property. The default lower limit is 0 and can never be less than 0 039 * <li>an optional <i>upper limit</i> - specifies the biggest duration permitted 040 * by the property. By default, there is no upper limit 041 * <li>support for <i>unlimited</i> durations - when permitted users can specify 042 * "unlimited" durations. These are represented using the decoded value, -1, or 043 * the encoded string value "unlimited". By default, unlimited durations are not 044 * permitted. In addition, it is not possible to define an upper limit and 045 * support unlimited values. 046 * </ul> 047 * Decoded values are represented using <code>long</code> values in the base 048 * unit defined for the duration property definition. 049 */ 050public final class DurationPropertyDefinition extends PropertyDefinition<Long> { 051 052 /** String used to represent unlimited durations. */ 053 private static final String UNLIMITED = "unlimited"; 054 055 /** The base unit for this property definition. */ 056 private final DurationUnit baseUnit; 057 058 /** The optional maximum unit for this property definition. */ 059 private final DurationUnit maximumUnit; 060 061 /** The lower limit of the property value in milli-seconds. */ 062 private final long lowerLimit; 063 064 /** The optional upper limit of the property value in milli-seconds. */ 065 private final Long upperLimit; 066 067 /** 068 * Indicates whether this property allows the use of the "unlimited" 069 * duration value (represented using a -1L or the string 070 * "unlimited"). 071 */ 072 private final boolean allowUnlimited; 073 074 /** An interface for incrementally constructing duration property definitions. */ 075 public static final class Builder extends AbstractBuilder<Long, DurationPropertyDefinition> { 076 077 /** The base unit for this property definition. */ 078 private DurationUnit baseUnit = DurationUnit.SECONDS; 079 080 /** The optional maximum unit for this property definition. */ 081 private DurationUnit maximumUnit; 082 083 /** The lower limit of the property value in milli-seconds. */ 084 private long lowerLimit; 085 086 /** The optional upper limit of the property value in milli-seconds. */ 087 private Long upperLimit; 088 089 /** 090 * Indicates whether this property allows the use of the 091 * "unlimited" duration value (represented using a -1L or the 092 * string "unlimited"). 093 */ 094 private boolean allowUnlimited; 095 096 /** Private constructor. */ 097 private Builder(AbstractManagedObjectDefinition<?, ?> d, String propertyName) { 098 super(d, propertyName); 099 } 100 101 /** 102 * Set the base unit for this property definition (values including 103 * limits are specified in this unit). By default a duration property 104 * definition uses seconds. 105 * 106 * @param unit 107 * The string representation of the base unit (must not be 108 * <code>null</code>). 109 * @throws IllegalArgumentException 110 * If the provided unit name did not correspond to a known 111 * duration unit, or if the base unit is bigger than the 112 * maximum unit. 113 */ 114 public final void setBaseUnit(String unit) { 115 Reject.ifNull(unit); 116 117 setBaseUnit(DurationUnit.getUnit(unit)); 118 } 119 120 /** 121 * Set the base unit for this property definition (values including 122 * limits are specified in this unit). By default a duration property 123 * definition uses seconds. 124 * 125 * @param unit 126 * The base unit (must not be <code>null</code>). 127 * @throws IllegalArgumentException 128 * If the provided base unit is bigger than the maximum 129 * unit. 130 */ 131 public final void setBaseUnit(DurationUnit unit) { 132 Reject.ifNull(unit); 133 134 // Make sure that the base unit is not bigger than the maximum unit. 135 if (maximumUnit != null && unit.getDuration() > maximumUnit.getDuration()) { 136 throw new IllegalArgumentException("Base unit greater than maximum unit"); 137 } 138 139 this.baseUnit = unit; 140 } 141 142 /** 143 * Set the maximum unit for this property definition. By default there 144 * is no maximum unit. 145 * 146 * @param unit 147 * The string representation of the maximum unit, or 148 * <code>null</code> if there should not be a maximum unit. 149 * @throws IllegalArgumentException 150 * If the provided unit name did not correspond to a known 151 * duration unit, or if the maximum unit is smaller than the 152 * base unit. 153 */ 154 public final void setMaximumUnit(String unit) { 155 setMaximumUnit(unit != null ? DurationUnit.getUnit(unit) : null); 156 } 157 158 /** 159 * Set the maximum unit for this property definition. By default there 160 * is no maximum unit. 161 * 162 * @param unit 163 * The maximum unit, or <code>null</code> if there should not 164 * be a maximum unit. 165 * @throws IllegalArgumentException 166 * If the provided maximum unit is smaller than the base 167 * unit. 168 */ 169 public final void setMaximumUnit(DurationUnit unit) { 170 // Make sure that the maximum unit is not smaller than the base unit. 171 if (unit != null && unit.getDuration() < baseUnit.getDuration()) { 172 throw new IllegalArgumentException("Maximum unit smaller than base unit"); 173 } 174 175 this.maximumUnit = unit; 176 } 177 178 /** 179 * Set the lower limit in milli-seconds. 180 * 181 * @param lowerLimit 182 * The new lower limit (must be >= 0) in milli-seconds. 183 * @throws IllegalArgumentException 184 * If a negative lower limit was specified, or the lower 185 * limit is greater than the upper limit. 186 */ 187 public final void setLowerLimit(long lowerLimit) { 188 if (lowerLimit < 0) { 189 throw new IllegalArgumentException("Negative lower limit"); 190 } 191 192 if (upperLimit != null && lowerLimit > upperLimit) { 193 throw new IllegalArgumentException("Lower limit greater than upper limit"); 194 } 195 196 this.lowerLimit = lowerLimit; 197 } 198 199 /** 200 * Set the lower limit using a string representation of the limit. If 201 * the string does not specify a unit, the current base unit will be 202 * used. 203 * 204 * @param lowerLimit 205 * The string representation of the new lower limit. 206 * @throws IllegalArgumentException 207 * If the lower limit could not be parsed, or if a negative 208 * lower limit was specified, or the lower limit is greater 209 * than the upper limit. 210 */ 211 public final void setLowerLimit(String lowerLimit) { 212 setLowerLimit(DurationUnit.parseValue(lowerLimit, baseUnit)); 213 } 214 215 /** 216 * Set the upper limit in milli-seconds. 217 * 218 * @param upperLimit 219 * The new upper limit in milli-seconds, or <code>null</code> 220 * if there is no upper limit. 221 * @throws IllegalArgumentException 222 * If a negative upper limit was specified, or the lower 223 * limit is greater than the upper limit or unlimited 224 * durations are permitted. 225 */ 226 public final void setUpperLimit(Long upperLimit) { 227 if (upperLimit != null) { 228 if (upperLimit < 0) { 229 throw new IllegalArgumentException("Negative upper limit"); 230 } 231 232 if (lowerLimit > upperLimit) { 233 throw new IllegalArgumentException("Lower limit greater than upper limit"); 234 } 235 236 if (allowUnlimited) { 237 throw new IllegalArgumentException("Upper limit specified when unlimited durations are permitted"); 238 } 239 } 240 241 this.upperLimit = upperLimit; 242 } 243 244 /** 245 * Set the upper limit using a string representation of the limit. If 246 * the string does not specify a unit, the current base unit will be 247 * used. 248 * 249 * @param upperLimit 250 * The string representation of the new upper limit, or 251 * <code>null</code> if there is no upper limit. 252 * @throws IllegalArgumentException 253 * If the upper limit could not be parsed, or if the lower 254 * limit is greater than the upper limit. 255 */ 256 public final void setUpperLimit(String upperLimit) { 257 setUpperLimit(upperLimit != null ? DurationUnit.parseValue(upperLimit, baseUnit) : null); 258 } 259 260 /** 261 * Specify whether this property definition will allow unlimited 262 * values (default is false). 263 * 264 * @param allowUnlimited 265 * <code>true</code> if the property will allow unlimited 266 * values, or <code>false</code> otherwise. 267 * @throws IllegalArgumentException 268 * If unlimited values are to be permitted but there is an 269 * upper limit specified. 270 */ 271 public final void setAllowUnlimited(boolean allowUnlimited) { 272 if (allowUnlimited && upperLimit != null) { 273 throw new IllegalArgumentException("Upper limit specified when unlimited durations are permitted"); 274 } 275 276 this.allowUnlimited = allowUnlimited; 277 } 278 279 @Override 280 protected DurationPropertyDefinition buildInstance(AbstractManagedObjectDefinition<?, ?> d, 281 String propertyName, EnumSet<PropertyOption> options, AdministratorAction adminAction, 282 DefaultBehaviorProvider<Long> defaultBehavior) { 283 return new DurationPropertyDefinition(d, propertyName, options, adminAction, defaultBehavior, baseUnit, 284 maximumUnit, lowerLimit, upperLimit, allowUnlimited); 285 } 286 } 287 288 /** 289 * Create a duration property definition builder. 290 * 291 * @param d 292 * The managed object definition associated with this property 293 * definition. 294 * @param propertyName 295 * The property name. 296 * @return Returns the new integer property definition builder. 297 */ 298 public static Builder createBuilder(AbstractManagedObjectDefinition<?, ?> d, String propertyName) { 299 return new Builder(d, propertyName); 300 } 301 302 /** Private constructor. */ 303 private DurationPropertyDefinition(AbstractManagedObjectDefinition<?, ?> d, String propertyName, 304 EnumSet<PropertyOption> options, AdministratorAction adminAction, 305 DefaultBehaviorProvider<Long> defaultBehavior, DurationUnit baseUnit, DurationUnit maximumUnit, 306 Long lowerLimit, Long upperLimit, boolean allowUnlimited) { 307 super(d, Long.class, propertyName, options, adminAction, defaultBehavior); 308 this.baseUnit = baseUnit; 309 this.maximumUnit = maximumUnit; 310 this.lowerLimit = lowerLimit; 311 this.upperLimit = upperLimit; 312 this.allowUnlimited = allowUnlimited; 313 } 314 315 /** 316 * Get the base unit for this property definition (values including limits 317 * are specified in this unit). 318 * 319 * @return Returns the base unit for this property definition (values 320 * including limits are specified in this unit). 321 */ 322 public DurationUnit getBaseUnit() { 323 return baseUnit; 324 } 325 326 /** 327 * Get the maximum unit for this property definition if specified. 328 * 329 * @return Returns the maximum unit for this property definition, or 330 * <code>null</code> if there is no maximum unit. 331 */ 332 public DurationUnit getMaximumUnit() { 333 return maximumUnit; 334 } 335 336 /** 337 * Get the lower limit in milli-seconds. 338 * 339 * @return Returns the lower limit in milli-seconds. 340 */ 341 public long getLowerLimit() { 342 return lowerLimit; 343 } 344 345 /** 346 * Get the upper limit in milli-seconds. 347 * 348 * @return Returns the upper limit in milli-seconds, or <code>null</code> if 349 * there is no upper limit. 350 */ 351 public Long getUpperLimit() { 352 return upperLimit; 353 } 354 355 /** 356 * Determine whether this property allows unlimited durations. 357 * 358 * @return Returns <code>true</code> if this this property allows unlimited 359 * durations. 360 */ 361 public boolean isAllowUnlimited() { 362 return allowUnlimited; 363 } 364 365 @Override 366 public void validateValue(Long value) { 367 Reject.ifNull(value); 368 369 long nvalue = baseUnit.toMilliSeconds(value); 370 if (!allowUnlimited && nvalue < lowerLimit) { 371 throw PropertyException.illegalPropertyValueException(this, value); 372 373 // unlimited allowed 374 } else if (nvalue >= 0 && nvalue < lowerLimit) { 375 throw PropertyException.illegalPropertyValueException(this, value); 376 } 377 378 if (upperLimit != null && nvalue > upperLimit) { 379 throw PropertyException.illegalPropertyValueException(this, value); 380 } 381 } 382 383 @Override 384 public String encodeValue(Long value) { 385 Reject.ifNull(value); 386 387 // Make sure that we correctly encode negative values as "unlimited". 388 if (allowUnlimited && value < 0) { 389 return UNLIMITED; 390 } 391 392 // Encode the size value using the base unit. 393 StringBuilder builder = new StringBuilder(); 394 builder.append(value); 395 builder.append(' '); 396 builder.append(baseUnit); 397 return builder.toString(); 398 } 399 400 @Override 401 public Long decodeValue(String value) { 402 Reject.ifNull(value); 403 404 // First check for the special "unlimited" value when necessary. 405 if (allowUnlimited && UNLIMITED.equalsIgnoreCase(value.trim())) { 406 return -1L; 407 } 408 409 // Parse the string representation. 410 long ms; 411 try { 412 ms = DurationUnit.parseValue(value); 413 } catch (NumberFormatException e) { 414 throw PropertyException.illegalPropertyValueException(this, value); 415 } 416 417 // Check the unit is in range - values must not be more granular 418 // than the base unit. 419 if (ms % baseUnit.getDuration() != 0) { 420 throw PropertyException.illegalPropertyValueException(this, value); 421 } 422 423 // Convert the value a long in the property's required unit. 424 Long i = (long) baseUnit.fromMilliSeconds(ms); 425 try { 426 validateValue(i); 427 return i; 428 } catch (PropertyException e) { 429 throw PropertyException.illegalPropertyValueException(this, value); 430 } 431 } 432 433 @Override 434 public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) { 435 return v.visitDuration(this, p); 436 } 437 438 @Override 439 public <R, P> R accept(PropertyValueVisitor<R, P> v, Long value, P p) { 440 return v.visitDuration(this, value, p); 441 } 442 443 @Override 444 public void toString(StringBuilder builder) { 445 super.toString(builder); 446 447 builder.append(" baseUnit="); 448 builder.append(baseUnit); 449 450 if (maximumUnit != null) { 451 builder.append(" maximumUnit="); 452 builder.append(maximumUnit); 453 } 454 455 builder.append(" lowerLimit="); 456 builder.append(lowerLimit); 457 builder.append("ms"); 458 459 if (upperLimit != null) { 460 builder.append(" upperLimit="); 461 builder.append(upperLimit); 462 builder.append("ms"); 463 } 464 465 builder.append(" allowUnlimited="); 466 builder.append(allowUnlimited); 467 } 468 469 @Override 470 public int compare(Long o1, Long o2) { 471 return o1.compareTo(o2); 472 } 473 474}