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