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