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