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