/*
|
* CDDL HEADER START
|
*
|
* The contents of this file are subject to the terms of the
|
* Common Development and Distribution License, Version 1.0 only
|
* (the "License"). You may not use this file except in compliance
|
* with the License.
|
*
|
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
|
* or http://forgerock.org/license/CDDLv1.0.html.
|
* See the License for the specific language governing permissions
|
* and limitations under the License.
|
*
|
* When distributing Covered Code, include this CDDL HEADER in each
|
* file and include the License file at legal-notices/CDDLv1_0.txt.
|
* If applicable, add the following below this CDDL HEADER, with the
|
* fields enclosed by brackets "[]" replaced with your own identifying
|
* information:
|
* Portions Copyright [yyyy] [name of copyright owner]
|
*
|
* CDDL HEADER END
|
*
|
*
|
* Copyright 2006-2008 Sun Microsystems, Inc.
|
* Portions Copyright 2014 ForgeRock AS
|
*/
|
package org.opends.server.config;
|
import org.forgerock.i18n.LocalizableMessage;
|
|
|
|
import java.util.ArrayList;
|
import java.util.HashMap;
|
import java.util.Iterator;
|
import java.util.LinkedHashSet;
|
import java.util.List;
|
|
import javax.management.AttributeList;
|
import javax.management.MBeanAttributeInfo;
|
import javax.management.MBeanParameterInfo;
|
|
import org.opends.server.api.AttributeSyntax;
|
import org.opends.server.core.DirectoryServer;
|
import org.opends.server.types.*;
|
import org.forgerock.opendj.ldap.ByteString;
|
import static org.opends.server.config.ConfigConstants.*;
|
import org.forgerock.i18n.slf4j.LocalizedLogger;
|
import static org.opends.messages.ConfigMessages.*;
|
/**
|
* This class defines a configuration attribute that stores both an integer
|
* value and an associated unit. The unit will contain both a string and a
|
* floating-point value. When a unit is selected, then the associated value
|
* will be used as a multiplier for the integer value to achieve the actual
|
* value for this parameter. For example, the attribute could be used to
|
* specify a size in bytes, but a value with a unit of "kb" could multiply that
|
* value by 1024, or "mb" by 1048576, or "gb" by 1073741824. In this case, a
|
* value of "50 gb" would be the logical equivalent of "53687091200 b". Upper
|
* and lower bounds may be imposed, and in that case they will be imposed on
|
* the actual value not on merely the integer portion. This attribute may only
|
* hold a single value and it will always be required.
|
*/
|
@org.opends.server.types.PublicAPI(
|
stability=org.opends.server.types.StabilityLevel.VOLATILE,
|
mayInstantiate=true,
|
mayExtend=false,
|
mayInvoke=true)
|
public final class IntegerWithUnitConfigAttribute
|
extends ConfigAttribute
|
{
|
private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
|
|
|
|
|
// Indicates whether this configuration attribute should impose a lower bound
|
// for the calculated value.
|
private boolean hasLowerBound;
|
|
// Indicates whether this configuration attribute should impose an upper bound
|
// for the calculated value.
|
private boolean hasUpperBound;
|
|
// The set of unit names and associated multipliers.
|
private HashMap<String,Double> units;
|
|
// The active calculated value for this attribute.
|
private long activeCalculatedValue;
|
|
// The active value for this attribute.
|
private long activeIntValue;
|
|
// The lower bound for the calculated value.
|
private long lowerBound;
|
|
// The pending calculated value for this attribute.
|
private long pendingCalculatedValue;
|
|
// The the pending value for this attribute.
|
private long pendingIntValue;
|
|
// The upper bound for the calculated value.
|
private long upperBound;
|
|
// The active unit for this attribute.
|
private String activeUnit;
|
|
// The pending unit for this attribute.
|
private String pendingUnit;
|
|
|
|
/**
|
* Creates a new integer with unit configuration attribute stub with the
|
* provided information but no values. The values will be set using the
|
* <CODE>setInitialValue</CODE> method. Mo validation will be performed on
|
* the set of allowed units.
|
*
|
* @param name The name for this configuration attribute.
|
* @param description The description for this configuration
|
* attribute.
|
* @param requiresAdminAction Indicates whether changes to this
|
* configuration attribute require administrative
|
* action before they will take effect.
|
* @param units The set of units and their associated
|
* multipliers for this configuration attribute.
|
* @param hasLowerBound Indicates whether a lower bound will be
|
* enforced for the calculated value.
|
* @param lowerBound The lower bound for the calculated value.
|
* @param hasUpperBound Indicates whether an upper bound will be
|
* enforced for the calculated value.
|
* @param upperBound The upper bound for the calculated value.
|
*/
|
public IntegerWithUnitConfigAttribute(String name, LocalizableMessage description,
|
boolean requiresAdminAction,
|
HashMap<String,Double> units,
|
boolean hasLowerBound, long lowerBound,
|
boolean hasUpperBound, long upperBound)
|
{
|
super(name, description, true, false, requiresAdminAction);
|
|
|
this.units = units;
|
this.hasLowerBound = hasLowerBound;
|
this.lowerBound = lowerBound;
|
this.hasUpperBound = hasUpperBound;
|
this.upperBound = upperBound;
|
}
|
|
|
|
/**
|
* Creates a new integer with unit configuration attribute with the provided
|
* information. No validation will be performed on the provided value or
|
* unit, or on the set of allowed units.
|
*
|
* @param name The name for this configuration attribute.
|
* @param description The description for this configuration
|
* attribute.
|
* @param requiresAdminAction Indicates whether changes to this
|
* configuration attribute require administrative
|
* action before they will take effect.
|
* @param units The set of units and their associated
|
* multipliers for this configuration attribute.
|
* @param hasLowerBound Indicates whether a lower bound will be
|
* enforced for the calculated value.
|
* @param lowerBound The lower bound for the calculated value.
|
* @param hasUpperBound Indicates whether an upper bound will be
|
* enforced for the calculated value.
|
* @param upperBound The upper bound for the calculated value.
|
* @param intValue The selected value for this configuration
|
* attribute.
|
* @param selectedUnit The selected unit for this configuration
|
* attribute.
|
*/
|
public IntegerWithUnitConfigAttribute(String name, LocalizableMessage description,
|
boolean requiresAdminAction,
|
HashMap<String,Double> units,
|
boolean hasLowerBound, long lowerBound,
|
boolean hasUpperBound, long upperBound,
|
long intValue, String selectedUnit)
|
{
|
super(name, description, true, false, requiresAdminAction,
|
getValueSet(intValue, selectedUnit));
|
|
|
|
this.units = units;
|
this.hasLowerBound = hasLowerBound;
|
this.lowerBound = lowerBound;
|
this.hasUpperBound = hasUpperBound;
|
this.upperBound = upperBound;
|
this.activeIntValue = intValue;
|
this.activeUnit = selectedUnit;
|
|
pendingIntValue = activeIntValue;
|
pendingUnit = activeUnit;
|
|
if (units.containsKey(selectedUnit))
|
{
|
activeCalculatedValue = (long) (activeIntValue * units.get(selectedUnit));
|
}
|
|
pendingCalculatedValue = activeCalculatedValue;
|
}
|
|
|
|
/**
|
* Creates a new integer with unit configuration attribute with the provided
|
* information. No validation will be performed on the provided value or
|
* unit, or on the set of allowed units.
|
*
|
* @param name The name for this configuration attribute.
|
* @param description The description for this configuration
|
* attribute.
|
* @param requiresAdminAction Indicates whether changes to this
|
* configuration attribute require administrative
|
* action before they will take effect.
|
* @param units The set of units and their associated
|
* multipliers for this configuration attribute.
|
* @param hasLowerBound Indicates whether a lower bound will be
|
* enforced for the calculated value.
|
* @param lowerBound The lower bound for the calculated value.
|
* @param hasUpperBound Indicates whether an upper bound will be
|
* enforced for the calculated value.
|
* @param upperBound The upper bound for the calculated value.
|
* @param activeIntValue The active selected value for this
|
* configuration attribute.
|
* @param activeSelectedUnit The active selected unit for this
|
* configuration attribute.
|
* @param pendingIntValue The pending selected value for this
|
* configuration attribute.
|
* @param pendingSelectedUnit The pending selected unit for this
|
* configuration attribute.
|
*/
|
public IntegerWithUnitConfigAttribute(String name, LocalizableMessage description,
|
boolean requiresAdminAction,
|
HashMap<String,Double> units,
|
boolean hasLowerBound, long lowerBound,
|
boolean hasUpperBound, long upperBound,
|
long activeIntValue,
|
String activeSelectedUnit,
|
long pendingIntValue,
|
String pendingSelectedUnit)
|
{
|
super(name, description, true, false, requiresAdminAction,
|
getValueSet(activeIntValue, activeSelectedUnit),
|
(pendingSelectedUnit != null),
|
getValueSet(pendingIntValue,pendingSelectedUnit));
|
|
|
|
this.units = units;
|
this.hasLowerBound = hasLowerBound;
|
this.lowerBound = lowerBound;
|
this.hasUpperBound = hasUpperBound;
|
this.upperBound = upperBound;
|
this.activeIntValue = activeIntValue;
|
this.activeUnit = activeSelectedUnit;
|
|
if (pendingSelectedUnit == null)
|
{
|
this.pendingIntValue = activeIntValue;
|
this.pendingUnit = activeUnit;
|
}
|
else
|
{
|
this.pendingIntValue = pendingIntValue;
|
this.pendingUnit = pendingSelectedUnit;
|
}
|
|
if (units.containsKey(activeUnit))
|
{
|
activeCalculatedValue = (long) (activeIntValue*units.get(activeUnit));
|
}
|
|
|
if (units.containsKey(pendingUnit))
|
{
|
pendingCalculatedValue = (long) (pendingIntValue*units.get(pendingUnit));
|
}
|
}
|
|
|
|
/**
|
* Retrieves the name of the data type for this configuration attribute. This
|
* is for informational purposes (e.g., inclusion in method signatures and
|
* other kinds of descriptions) and does not necessarily need to map to an
|
* actual Java type.
|
*
|
* @return The name of the data type for this configuration attribute.
|
*/
|
public String getDataType()
|
{
|
return "IntegerWithUnit";
|
}
|
|
|
|
/**
|
* Retrieves the attribute syntax for this configuration attribute.
|
*
|
* @return The attribute syntax for this configuration attribute.
|
*/
|
public AttributeSyntax<?> getSyntax()
|
{
|
return DirectoryServer.getDefaultStringSyntax();
|
}
|
|
|
|
/**
|
* Retrieves the integer component of the active value for this configuration
|
* attribute.
|
*
|
* @return The integer component of the active value for this configuration
|
* attribute.
|
*/
|
public long activeIntValue()
|
{
|
return activeIntValue;
|
}
|
|
|
|
/**
|
* Retrieves the name of the active unit for this configuration attribute.
|
*
|
* @return The name of the active unit for this configuration attribute.
|
*/
|
public String activeUnit()
|
{
|
return activeUnit;
|
}
|
|
|
|
/**
|
* Retrieves the calculated active value for this configuration attribute.
|
* This will be the product of the active int value and the multiplier for the
|
* associated active unit.
|
*
|
* @return The calculated active value for this configuration attribute.
|
*/
|
public long activeCalculatedValue()
|
{
|
return activeCalculatedValue;
|
}
|
|
|
|
/**
|
* Retrieves the integer component of the pending value for this configuration
|
* attribute. If there is no pending value, then the integer component of the
|
* active value will be returned.
|
*
|
* @return The integer component of the pending value for this configuration
|
* attribute.
|
*/
|
public long pendingIntValue()
|
{
|
if (hasPendingValues())
|
{
|
return pendingIntValue;
|
}
|
else
|
{
|
return activeIntValue;
|
}
|
}
|
|
|
|
/**
|
* Retrieves the name of the pending unit for this configuration attribute.
|
* If there is no pending value, then the unit for the active value will be
|
* returned.
|
*
|
* @return The name of the pending unit for this configuration attribute.
|
*/
|
public String pendingUnit()
|
{
|
if (hasPendingValues())
|
{
|
return pendingUnit;
|
}
|
else
|
{
|
return activeUnit;
|
}
|
}
|
|
|
|
/**
|
* Retrieves the calculated pending value for this configuration attribute.
|
* This will be the product of the pending int value and the multiplier for
|
* the associated pending unit. If there is no pending value, then the
|
* calculated active value will be returned.
|
*
|
* @return The calculated pending value for this configuration attribute.
|
*/
|
public long pendingCalculatedValue()
|
{
|
if (hasPendingValues())
|
{
|
return pendingCalculatedValue;
|
}
|
else
|
{
|
return activeCalculatedValue;
|
}
|
}
|
|
|
|
/**
|
* Retrieves the mapping between the allowed names for the units and their
|
* multipliers for this configuration attribute.
|
*
|
* @return The mapping between the allowed names for the units and their
|
* multipliers for this configuration attribute.
|
*/
|
public HashMap<String,Double> getUnits()
|
{
|
return units;
|
}
|
|
|
|
/**
|
* Indicates whether a lower bound will be enforced for the calculated value
|
* of this configuration attribute.
|
*
|
* @return <CODE>true</CODE> if a lower bound will be enforced for the
|
* calculated value of this configuration attribute, or
|
* <CODE>false</CODE> if not.
|
*/
|
public boolean hasLowerBound()
|
{
|
return hasLowerBound;
|
}
|
|
|
|
/**
|
* Retrieves the lower bound for the calculated value of this configuration
|
* attribute.
|
*
|
* @return The lower bound for the calculated value of this configuration
|
* attribute.
|
*/
|
public long getLowerBound()
|
{
|
return lowerBound;
|
}
|
|
|
|
/**
|
* Indicates whether an upper bound will be enforced for the calculated value
|
* of this configuration attribute.
|
*
|
* @return <CODE>true</CODE> if an upper bound will be enforced for the
|
* calculated value of this configuration attribute, or
|
* <CODE>false</CODE> if not.
|
*/
|
public boolean hasUpperBound()
|
{
|
return hasUpperBound;
|
}
|
|
|
|
/**
|
* Retrieves the upper bound for the calculated value of this configuration
|
* attribute.
|
*
|
* @return The upper bound for the calculated value of this configuration
|
* attribute.
|
*/
|
public long getUpperBound()
|
{
|
return upperBound;
|
}
|
|
|
|
/**
|
* Sets the value for this configuration attribute.
|
*
|
* @param intValue The integer component for the value of this configuration
|
* attribute.
|
* @param unit The unit for the value of this configuration attribute.
|
*
|
* @throws ConfigException If the provided unit is not recognized, or if the
|
* resulting calculated value is outside the
|
* acceptable bounds.
|
*/
|
public void setValue(long intValue, String unit)
|
throws ConfigException
|
{
|
if ((unit == null) || (! units.containsKey(unit)))
|
{
|
LocalizableMessage message = ERR_CONFIG_ATTR_INVALID_UNIT.get(unit, getName());
|
throw new ConfigException(message);
|
}
|
|
|
long calculatedValue = (long) (intValue * units.get(unit));
|
if (hasLowerBound && (calculatedValue < lowerBound))
|
{
|
LocalizableMessage message = ERR_CONFIG_ATTR_INT_BELOW_LOWER_BOUND.get(
|
getName(), calculatedValue, lowerBound);
|
throw new ConfigException(message);
|
}
|
|
if (hasUpperBound && (calculatedValue > upperBound))
|
{
|
LocalizableMessage message = ERR_CONFIG_ATTR_INT_ABOVE_UPPER_BOUND.get(
|
getName(), calculatedValue, upperBound);
|
throw new ConfigException(message);
|
}
|
|
|
if (requiresAdminAction())
|
{
|
pendingCalculatedValue = calculatedValue;
|
pendingIntValue = intValue;
|
pendingUnit = unit;
|
setPendingValues(getValueSet(intValue, unit));
|
}
|
else
|
{
|
activeCalculatedValue = calculatedValue;
|
activeIntValue = intValue;
|
activeUnit = unit;
|
setActiveValues(getValueSet(intValue, unit));
|
}
|
}
|
|
|
|
/**
|
* Sets the value for this configuration attribute.
|
*
|
* @param value The string representation of the value to use for this
|
* configuration attribute.
|
*
|
* @throws ConfigException If the provided value is invalid for some reason.
|
*/
|
public void setValue(String value)
|
throws ConfigException
|
{
|
int spacePos = value.indexOf(' ');
|
if (spacePos <= 0)
|
{
|
LocalizableMessage message = ERR_CONFIG_ATTR_NO_UNIT_DELIMITER.get(
|
String.valueOf(value), getName());
|
throw new ConfigException(message);
|
}
|
|
|
long longValue;
|
try
|
{
|
longValue = Long.parseLong(value.substring(0, spacePos));
|
}
|
catch (Exception e)
|
{
|
logger.traceException(e);
|
|
LocalizableMessage message = ERR_CONFIG_ATTR_COULD_NOT_PARSE_INT_COMPONENT.get(
|
String.valueOf(value), getName(), String.valueOf(e));
|
throw new ConfigException(message, e);
|
}
|
|
setValue(longValue, value.substring(spacePos+1));
|
}
|
|
|
|
/**
|
* Creates the appropriate value set with the provided value.
|
*
|
* @param intValue The integer component for the value to construct.
|
* @param unit The unit name for the value to construct.
|
*
|
* @return The constructed value set.
|
*/
|
private static LinkedHashSet<AttributeValue> getValueSet(long intValue,
|
String unit)
|
{
|
if (unit == null)
|
{
|
return null;
|
}
|
|
LinkedHashSet<AttributeValue> valueSet =
|
new LinkedHashSet<AttributeValue>(1);
|
|
String valueString = intValue + " " + unit;
|
valueSet.add(AttributeValues.create(ByteString.valueOf(valueString),
|
ByteString.valueOf(valueString)));
|
|
return valueSet;
|
}
|
|
|
|
/**
|
* Applies the set of pending values, making them the active values for this
|
* configuration attribute. This will not take any action if there are no
|
* pending values.
|
*/
|
public void applyPendingValues()
|
{
|
if (! hasPendingValues())
|
{
|
return;
|
}
|
|
super.applyPendingValues();
|
activeCalculatedValue = pendingCalculatedValue;
|
activeIntValue = pendingIntValue;
|
activeUnit = pendingUnit;
|
}
|
|
|
|
/**
|
* Indicates whether the provided value is acceptable for use in this
|
* attribute. If it is not acceptable, then the reason should be written into
|
* the provided buffer.
|
*
|
* @param value The value for which to make the determination.
|
* @param rejectReason A buffer into which a human-readable reason for the
|
* reject may be written.
|
*
|
* @return <CODE>true</CODE> if the provided value is acceptable for use in
|
* this attribute, or <CODE>false</CODE> if not.
|
*/
|
public boolean valueIsAcceptable(AttributeValue value,
|
StringBuilder rejectReason)
|
{
|
// Get a string representation of the value and convert it to lowercase.
|
String lowerValue = value.getValue().toString().toLowerCase();
|
|
return valueIsAcceptable(lowerValue, rejectReason);
|
}
|
|
|
|
/**
|
* Indicates whether the provided value is acceptable for use in this
|
* attribute. If it is not acceptable, then the reason should be written into
|
* the provided buffer.
|
*
|
* @param lowerValue The lowercase version of the value for which to make
|
* the determination.
|
* @param rejectReason A buffer into which a human-readable reason for the
|
* reject may be written.
|
*
|
* @return <CODE>true</CODE> if the provided value is acceptable for use in
|
* this attribute, or <CODE>false</CODE> if not.
|
*/
|
public boolean valueIsAcceptable(String lowerValue,
|
StringBuilder rejectReason)
|
{
|
// Find the first space in the value, since it should separate the integer
|
// from the unit.
|
int spacePos = lowerValue.indexOf(' ');
|
if (spacePos < 0)
|
{
|
rejectReason.append(ERR_CONFIG_ATTR_NO_UNIT_DELIMITER.get(
|
lowerValue, getName()));
|
return false;
|
}
|
|
|
// The part up to the space should be the integer component.
|
long longValue;
|
try
|
{
|
longValue = Long.parseLong(lowerValue.substring(0, spacePos));
|
}
|
catch (Exception e)
|
{
|
logger.traceException(e);
|
|
rejectReason.append(ERR_CONFIG_ATTR_INT_COULD_NOT_PARSE.get(
|
lowerValue, getName(), String.valueOf(e)));
|
return false;
|
}
|
|
|
// The rest of the value should be the unit. See if it is in the set of
|
// available units.
|
String unit = lowerValue.substring(spacePos+1);
|
double multiplier;
|
if (! units.containsKey(unit))
|
{
|
rejectReason.append(ERR_CONFIG_ATTR_INVALID_UNIT.get(unit,
|
getName()));
|
return false;
|
}
|
else
|
{
|
multiplier = units.get(unit);
|
}
|
|
|
// Multiply the int value by the unit multiplier and see if that is within
|
// the specified bounds.
|
long calculatedValue = (long) (longValue * multiplier);
|
if (hasLowerBound && (calculatedValue < lowerBound))
|
{
|
rejectReason.append(ERR_CONFIG_ATTR_INT_BELOW_LOWER_BOUND.get(
|
getName(), calculatedValue, lowerBound));
|
return false;
|
}
|
|
if (hasUpperBound && (calculatedValue > upperBound))
|
{
|
rejectReason.append(ERR_CONFIG_ATTR_INT_ABOVE_UPPER_BOUND.get(
|
getName(), calculatedValue, upperBound));
|
return false;
|
}
|
|
|
// If we've gotten here, then the value is OK.
|
return true;
|
}
|
|
|
|
/**
|
* Converts the provided set of strings to a corresponding set of attribute
|
* values.
|
*
|
* @param valueStrings The set of strings to be converted into attribute
|
* values.
|
* @param allowFailures Indicates whether the decoding process should allow
|
* any failures in which one or more values could be
|
* decoded but at least one could not. If this is
|
* <CODE>true</CODE> and such a condition is acceptable
|
* for the underlying attribute type, then the returned
|
* set of values should simply not include those
|
* undecodable values.
|
*
|
* @return The set of attribute values converted from the provided strings.
|
*
|
* @throws ConfigException If an unrecoverable problem occurs while
|
* performing the conversion.
|
*/
|
public LinkedHashSet<AttributeValue>
|
stringsToValues(List<String> valueStrings, boolean allowFailures)
|
throws ConfigException
|
{
|
if ((valueStrings == null) || valueStrings.isEmpty())
|
{
|
if (isRequired())
|
{
|
LocalizableMessage message = ERR_CONFIG_ATTR_IS_REQUIRED.get(getName());
|
throw new ConfigException(message);
|
}
|
else
|
{
|
return new LinkedHashSet<AttributeValue>();
|
}
|
}
|
|
|
int numValues = valueStrings.size();
|
if ((! isMultiValued()) && (numValues > 1))
|
{
|
LocalizableMessage message =
|
ERR_CONFIG_ATTR_SET_VALUES_IS_SINGLE_VALUED.get(getName());
|
throw new ConfigException(message);
|
}
|
|
|
LinkedHashSet<AttributeValue> valueSet =
|
new LinkedHashSet<AttributeValue>(numValues);
|
for (String valueString : valueStrings)
|
{
|
if ((valueString == null) || (valueString.length() == 0))
|
{
|
LocalizableMessage message = ERR_CONFIG_ATTR_EMPTY_STRING_VALUE.get(getName());
|
if (allowFailures)
|
{
|
logger.error(message);
|
continue;
|
}
|
else
|
{
|
throw new ConfigException(message);
|
}
|
}
|
|
|
StringBuilder rejectReason = new StringBuilder();
|
if (! valueIsAcceptable(valueString.toLowerCase(), rejectReason))
|
{
|
LocalizableMessage message = ERR_CONFIG_ATTR_INVALID_VALUE_WITH_UNIT.get(
|
valueString, getName(),
|
rejectReason.toString());
|
|
if (allowFailures)
|
{
|
logger.error(message);
|
continue;
|
}
|
else
|
{
|
throw new ConfigException(message);
|
}
|
}
|
|
|
valueSet.add(AttributeValues.create(ByteString.valueOf(valueString),
|
ByteString.valueOf(valueString)));
|
}
|
|
|
// If this method was configured to continue on error, then it is possible
|
// that we ended up with an empty list. Check to see if this is a required
|
// attribute and if so deal with it accordingly.
|
if ((isRequired()) && valueSet.isEmpty())
|
{
|
LocalizableMessage message = ERR_CONFIG_ATTR_IS_REQUIRED.get(getName());
|
throw new ConfigException(message);
|
}
|
|
|
return valueSet;
|
}
|
|
|
|
/**
|
* Converts the set of active values for this configuration attribute into a
|
* set of strings that may be stored in the configuration or represented over
|
* protocol. The string representation used by this method should be
|
* compatible with the decoding used by the <CODE>stringsToValues</CODE>
|
* method.
|
*
|
* @return The string representations of the set of active values for this
|
* configuration attribute.
|
*/
|
public List<String> activeValuesToStrings()
|
{
|
ArrayList<String> valueStrings = new ArrayList<String>(1);
|
valueStrings.add(activeIntValue + " " + activeUnit);
|
|
return valueStrings;
|
}
|
|
|
|
/**
|
* Converts the set of pending values for this configuration attribute into a
|
* set of strings that may be stored in the configuration or represented over
|
* protocol. The string representation used by this method should be
|
* compatible with the decoding used by the <CODE>stringsToValues</CODE>
|
* method.
|
*
|
* @return The string representations of the set of pending values for this
|
* configuration attribute, or <CODE>null</CODE> if there are no
|
* pending values.
|
*/
|
public List<String> pendingValuesToStrings()
|
{
|
if (hasPendingValues())
|
{
|
ArrayList<String> valueStrings = new ArrayList<String>(1);
|
valueStrings.add(pendingIntValue + " " + pendingUnit);
|
|
return valueStrings;
|
}
|
else
|
{
|
return null;
|
}
|
}
|
|
|
|
/**
|
* Retrieves a new configuration attribute of this type that will contain the
|
* values from the provided attribute.
|
*
|
* @param attributeList The list of attributes to use to create the config
|
* attribute. The list must contain either one or two
|
* elements, with both attributes having the same base
|
* name and the only option allowed is ";pending" and
|
* only if this attribute is one that requires admin
|
* action before a change may take effect.
|
*
|
* @return The generated configuration attribute.
|
*
|
* @throws ConfigException If the provided attribute cannot be treated as a
|
* configuration attribute of this type (e.g., if
|
* one or more of the values of the provided
|
* attribute are not suitable for an attribute of
|
* this type, or if this configuration attribute is
|
* single-valued and the provided attribute has
|
* multiple values).
|
*/
|
public ConfigAttribute getConfigAttribute(List<Attribute> attributeList)
|
throws ConfigException
|
{
|
long activeIntValue = 0;
|
long pendingIntValue = 0;
|
String activeUnit = null;
|
String pendingUnit = null;
|
|
for (Attribute a : attributeList)
|
{
|
if (a.hasOptions())
|
{
|
// This must be the pending value.
|
if (a.hasOption(OPTION_PENDING_VALUES))
|
{
|
if (pendingUnit != null)
|
{
|
// We cannot have multiple pending value sets.
|
LocalizableMessage message =
|
ERR_CONFIG_ATTR_MULTIPLE_PENDING_VALUE_SETS.get(a.getName());
|
throw new ConfigException(message);
|
}
|
|
|
if (a.isEmpty())
|
{
|
// This is illegal -- it must have a value.
|
LocalizableMessage message = ERR_CONFIG_ATTR_IS_REQUIRED.get(a.getName());
|
throw new ConfigException(message);
|
}
|
else
|
{
|
Iterator<AttributeValue> iterator = a.iterator();
|
|
String valueString = iterator.next().getValue().toString();
|
|
if (iterator.hasNext())
|
{
|
// This is illegal -- the attribute is single-valued.
|
LocalizableMessage message =
|
ERR_CONFIG_ATTR_SET_VALUES_IS_SINGLE_VALUED.get(a.getName());
|
throw new ConfigException(message);
|
}
|
|
try
|
{
|
int spacePos = valueString.indexOf(' ');
|
pendingIntValue =
|
Long.parseLong(valueString.substring(0, spacePos));
|
pendingUnit = valueString.substring(spacePos+1).trim();
|
}
|
catch (Exception e)
|
{
|
LocalizableMessage message = ERR_CONFIG_ATTR_COULD_NOT_PARSE_INT_COMPONENT.
|
get(valueString, a.getName(), String.valueOf(e));
|
throw new ConfigException(message);
|
}
|
|
|
// Get the unit and use it to determine the corresponding
|
// multiplier.
|
if (! units.containsKey(pendingUnit))
|
{
|
LocalizableMessage message =
|
ERR_CONFIG_ATTR_INVALID_UNIT.get(pendingUnit, a.getName());
|
throw new ConfigException(message);
|
}
|
|
double multiplier = units.get(activeUnit);
|
pendingCalculatedValue = (long) (multiplier * pendingIntValue);
|
|
|
// Check the bounds set for this attribute.
|
if (hasLowerBound && (pendingCalculatedValue < lowerBound))
|
{
|
LocalizableMessage message = ERR_CONFIG_ATTR_INT_BELOW_LOWER_BOUND.get(
|
a.getName(), pendingCalculatedValue, lowerBound);
|
throw new ConfigException(message);
|
}
|
|
if (hasUpperBound && (pendingCalculatedValue > upperBound))
|
{
|
LocalizableMessage message = ERR_CONFIG_ATTR_INT_ABOVE_UPPER_BOUND.get(
|
a.getName(), pendingCalculatedValue, upperBound);
|
throw new ConfigException(message);
|
}
|
}
|
}
|
else
|
{
|
// This is illegal -- only the pending option is allowed for
|
// configuration attributes.
|
LocalizableMessage message =
|
ERR_CONFIG_ATTR_OPTIONS_NOT_ALLOWED.get(a.getName());
|
throw new ConfigException(message);
|
}
|
}
|
else
|
{
|
// This must be the active value.
|
if (activeUnit != null)
|
{
|
// We cannot have multiple active value sets.
|
LocalizableMessage message =
|
ERR_CONFIG_ATTR_MULTIPLE_ACTIVE_VALUE_SETS.get(a.getName());
|
throw new ConfigException(message);
|
}
|
|
|
if (a.isEmpty())
|
{
|
// This is illegal -- it must have a value.
|
LocalizableMessage message = ERR_CONFIG_ATTR_IS_REQUIRED.get(a.getName());
|
throw new ConfigException(message);
|
}
|
else
|
{
|
Iterator<AttributeValue> iterator = a.iterator();
|
|
String valueString = iterator.next().getValue().toString();
|
|
if (iterator.hasNext())
|
{
|
// This is illegal -- the attribute is single-valued.
|
LocalizableMessage message =
|
ERR_CONFIG_ATTR_SET_VALUES_IS_SINGLE_VALUED.get(a.getName());
|
throw new ConfigException(message);
|
}
|
|
try
|
{
|
int spacePos = valueString.indexOf(' ');
|
activeIntValue =
|
Long.parseLong(valueString.substring(0, spacePos));
|
activeUnit = valueString.substring(spacePos+1).trim();
|
}
|
catch (Exception e)
|
{
|
LocalizableMessage message = ERR_CONFIG_ATTR_COULD_NOT_PARSE_INT_COMPONENT.get(
|
valueString, a.getName(), String.valueOf(e));
|
throw new ConfigException(message);
|
}
|
|
|
// Get the unit and use it to determine the corresponding multiplier.
|
if (! units.containsKey(activeUnit))
|
{
|
LocalizableMessage message =
|
ERR_CONFIG_ATTR_INVALID_UNIT.get(activeUnit, a.getName());
|
throw new ConfigException(message);
|
}
|
|
double multiplier = units.get(activeUnit);
|
activeCalculatedValue = (long) (multiplier * activeIntValue);
|
|
|
// Check the bounds set for this attribute.
|
if (hasLowerBound && (activeCalculatedValue < lowerBound))
|
{
|
LocalizableMessage message = ERR_CONFIG_ATTR_INT_BELOW_LOWER_BOUND.get(
|
a.getName(), activeCalculatedValue, lowerBound);
|
throw new ConfigException(message);
|
}
|
|
if (hasUpperBound && (activeCalculatedValue > upperBound))
|
{
|
LocalizableMessage message = ERR_CONFIG_ATTR_INT_ABOVE_UPPER_BOUND.get(
|
a.getName(), activeCalculatedValue, upperBound);
|
throw new ConfigException(message);
|
}
|
}
|
}
|
}
|
|
if (activeUnit == null)
|
{
|
// This is not OK. The value set must contain an active value.
|
LocalizableMessage message = ERR_CONFIG_ATTR_NO_ACTIVE_VALUE_SET.get(getName());
|
throw new ConfigException(message);
|
}
|
|
if (pendingUnit == null)
|
{
|
// This is OK. We'll just use the active value set.
|
pendingIntValue = activeIntValue;
|
pendingUnit = activeUnit;
|
}
|
|
|
return new IntegerWithUnitConfigAttribute(getName(), getDescription(),
|
requiresAdminAction(), units,
|
hasLowerBound, lowerBound,
|
hasUpperBound, upperBound,
|
activeIntValue, activeUnit,
|
pendingIntValue, pendingUnit);
|
}
|
|
|
|
/**
|
* Retrieves a JMX attribute containing the active value set for this
|
* configuration attribute.
|
*
|
* @return A JMX attribute containing the active value set for this
|
* configuration attribute, or <CODE>null</CODE> if it does not have
|
* any active values.
|
*/
|
public javax.management.Attribute toJMXAttribute()
|
{
|
return new javax.management.Attribute(getName(),
|
activeIntValue + " " + activeUnit);
|
}
|
|
/**
|
* Retrieves a JMX attribute containing the pending value set for this
|
* configuration attribute.
|
*
|
* @return A JMX attribute containing the pending value set for this
|
* configuration attribute, or <CODE>null</CODE> if it does not have
|
* any active values.
|
*/
|
public javax.management.Attribute toJMXAttributePending()
|
{
|
|
return new javax.management.Attribute(getName() + ";"
|
+ OPTION_PENDING_VALUES, pendingIntValue + " " + pendingUnit);
|
}
|
|
|
/**
|
* Adds information about this configuration attribute to the provided
|
* JMX attribute list. If this configuration attribute requires
|
* administrative action before changes take effect and it has a set of
|
* pending values, then two attributes should be added to the list --
|
* one for the active value and one for the pending value. The pending
|
* value should be named with the pending option.
|
*
|
* @param attributeList
|
* The attribute list to which the JMX attribute(s) should
|
* be added.
|
*/
|
public void toJMXAttribute(AttributeList attributeList)
|
{
|
String activeValue = activeIntValue + " " + activeUnit;
|
attributeList.add(new javax.management.Attribute(getName(), activeValue));
|
|
if (requiresAdminAction() &&
|
(pendingCalculatedValue != activeCalculatedValue))
|
{
|
String name = getName() + ";" + OPTION_PENDING_VALUES;
|
String pendingValue = pendingIntValue + " " + pendingUnit;
|
attributeList.add(new javax.management.Attribute(name, pendingValue));
|
}
|
}
|
|
|
|
/**
|
* Adds information about this configuration attribute to the provided list in
|
* the form of a JMX <CODE>MBeanAttributeInfo</CODE> object. If this
|
* configuration attribute requires administrative action before changes take
|
* effect and it has a set of pending values, then two attribute info objects
|
* should be added to the list -- one for the active value (which should be
|
* read-write) and one for the pending value (which should be read-only). The
|
* pending value should be named with the pending option.
|
*
|
* @param attributeInfoList The list to which the attribute information
|
* should be added.
|
*/
|
public void toJMXAttributeInfo(List<MBeanAttributeInfo> attributeInfoList)
|
{
|
attributeInfoList.add(new MBeanAttributeInfo(getName(),
|
String.class.getName(),
|
String.valueOf(
|
getDescription()),
|
true, true, false));
|
|
if (requiresAdminAction())
|
{
|
String name = getName() + ";" + OPTION_PENDING_VALUES;
|
attributeInfoList.add(new MBeanAttributeInfo(name,
|
String.class.getName(),
|
String.valueOf(
|
getDescription()),
|
true, false, false));
|
}
|
}
|
|
|
|
/**
|
* Retrieves a JMX <CODE>MBeanParameterInfo</CODE> object that describes this
|
* configuration attribute.
|
*
|
* @return A JMX <CODE>MBeanParameterInfo</CODE> object that describes this
|
* configuration attribute.
|
*/
|
public MBeanParameterInfo toJMXParameterInfo()
|
{
|
return new MBeanParameterInfo(getName(), String.class.getName(),
|
String.valueOf(getDescription()));
|
}
|
|
|
|
/**
|
* Attempts to set the value of this configuration attribute based on the
|
* information in the provided JMX attribute.
|
*
|
* @param jmxAttribute The JMX attribute to use to attempt to set the value
|
* of this configuration attribute.
|
*
|
* @throws ConfigException If the provided JMX attribute does not have an
|
* acceptable value for this configuration
|
* attribute.
|
*/
|
public void setValue(javax.management.Attribute jmxAttribute)
|
throws ConfigException
|
{
|
Object value = jmxAttribute.getValue();
|
if (value instanceof String)
|
{
|
setValue((String) value);
|
}
|
else
|
{
|
LocalizableMessage message = ERR_CONFIG_ATTR_INT_WITH_UNIT_INVALID_TYPE.get(
|
String.valueOf(value), getName(), value.getClass().getName());
|
throw new ConfigException(message);
|
}
|
}
|
|
|
|
/**
|
* Creates a duplicate of this configuration attribute.
|
*
|
* @return A duplicate of this configuration attribute.
|
*/
|
public ConfigAttribute duplicate()
|
{
|
return new IntegerWithUnitConfigAttribute(getName(), getDescription(),
|
requiresAdminAction(), units,
|
hasLowerBound, lowerBound,
|
hasUpperBound, upperBound,
|
activeIntValue, activeUnit,
|
pendingIntValue, pendingUnit);
|
}
|
}
|