| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package flex.messaging.config; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * The ConfigMap class is a helper implementation of Map that makes it easier |
| * to handle properties that can appear one or more times. If a property is set |
| * more than once, it is converted to a List and added as another property |
| * rather than replacing the existing property. It also provides utility APIs |
| * for getting properties from the Map, cast to a certain type and allows a |
| * default to be specified in the event that the property is missing. |
| */ |
| public class ConfigMap extends LinkedHashMap |
| { |
| /** |
| * This number was generated using the 'serialver' command line tool. |
| * This number should remain consistent with the version used by |
| * ColdFusion to communicate with the message broker over RMI. |
| */ |
| private static final long serialVersionUID = 8913604659150919550L; |
| |
| /** |
| * This error is thrown when a property unexpectedly contains multiple values. |
| */ |
| private static final int UNEXPECTED_MULTIPLE_VALUES = 10169; |
| |
| /** |
| * An *undocumented* system property can be used to revert to legacy config property handling: |
| * Legacy behavior - config property values were not trimmed, retaining leading/trailing whitespace which |
| * proved problematic for customers. |
| * New default behavior - config property values are trimmed. |
| */ |
| private static final String SYSPROPNAME_TRIM_CONFIG_PROPERTY_VALUES = "flex.trim-config-property-values"; |
| private static final boolean TRIM_CONFIG_PROPERTY_VALUES = Boolean.valueOf(System.getProperty(SYSPROPNAME_TRIM_CONFIG_PROPERTY_VALUES, "true")).booleanValue(); |
| |
| /** |
| * Map to keep track of accessed properties. |
| */ |
| private HashSet accessedKeys = new HashSet(); |
| |
| /** |
| * Constructs an empty <code>ConfigMap</code> with the default initial |
| * capacity of 10. |
| */ |
| public ConfigMap() |
| { |
| super(); |
| } |
| |
| /** |
| * Constructs a new <code>ConfigMap</code> with the initial |
| * capacity specified. |
| * |
| * @param initialCapacity the initial capacity. |
| */ |
| public ConfigMap(int initialCapacity) |
| { |
| super(initialCapacity); |
| } |
| |
| /** |
| * Constructs a new <code>ConfigMap</code> and copies the values |
| * from the supplied map to this map. |
| * |
| * @param m a <code>ConfigMap</code> whose properties are to be added to |
| * this <code>ConfigMap</code>. |
| */ |
| public ConfigMap(ConfigMap m) |
| { |
| this(); |
| addProperties(m); |
| } |
| |
| /** |
| * Adds all properties from a map to this map. |
| * |
| * @param p a <code>ConfigMap</code> whose properties are to be added to |
| * this <code>ConfigMap</code>. |
| */ |
| public void addProperties(ConfigMap p) |
| { |
| Iterator it = p.entrySet().iterator(); |
| while (it.hasNext()) |
| { |
| Map.Entry entry = (Map.Entry) it.next(); |
| Object key = entry.getKey(); |
| Object value = entry.getValue(); |
| if (value instanceof ValueList) |
| { |
| addProperties(key, (ValueList) value); |
| } |
| else |
| { |
| addPropertyLogic(key, value); |
| } |
| } |
| } |
| |
| /** |
| * Helper method to add a list of values under a key. |
| * |
| * @param key The key to add the values under. |
| * @param values The list of values to add. |
| */ |
| private void addProperties(Object key, ValueList values) |
| { |
| ValueList list = getValueList(key); |
| if (list == null) |
| { |
| put(key, values.clone()); |
| } |
| else |
| { |
| list.addAll(values); |
| } |
| } |
| |
| /** |
| * Helper method to add a value under a key. |
| * |
| * @param key The key to add the value under. |
| * @param value The value to add. |
| */ |
| private void addPropertyLogic(Object key, Object value) |
| { |
| ValueList list = getValueList(key); |
| if (list == null) |
| { |
| put(key, value); |
| } |
| else |
| { |
| list.add(value); |
| } |
| } |
| |
| private static class ValueList extends ArrayList |
| { |
| /** |
| * Serial version id. |
| */ |
| static final long serialVersionUID = -5637755312744414675L; |
| } |
| |
| /** |
| * Given a key, returns a list of values associated with that key. |
| * |
| * @param key The key. |
| * @return A list of values associated with the key. |
| */ |
| private ValueList getValueList(Object key) |
| { |
| ValueList list; |
| Object old = super.get(key); |
| if (old instanceof ValueList) |
| { |
| list = (ValueList) old; |
| } |
| else if (old != null) |
| { |
| list = new ValueList(); |
| list.add(old); |
| put(key, list); |
| } |
| else |
| { |
| list = null; |
| } |
| return list; |
| } |
| |
| /** |
| * Adds a <code>String</code> value to this map for the given property |
| * name. |
| * |
| * @param name the property name |
| * @param value the property value |
| */ |
| public void addProperty(String name, String value) |
| { |
| addPropertyLogic(name, TRIM_CONFIG_PROPERTY_VALUES && value != null ? value.trim() : value); |
| } |
| |
| /** |
| * Adds a <code>ConfigMap</code> value to this map for the given property |
| * name. |
| * |
| * @param name the property name |
| * @param value the property value |
| */ |
| public void addProperty(String name, ConfigMap value) |
| { |
| addPropertyLogic(name, value); |
| } |
| |
| /** |
| * Gets the set of property names contained in this map. |
| * |
| * @return a <code>Set</code> of property name <code>String</code>s. |
| */ |
| public Set propertyNames() |
| { |
| return keySet(); |
| } |
| |
| /** |
| * Sets a property name as allowed without needing to access the property |
| * value. This marks a property as allowed for validation purposes. |
| * |
| * @param name the property name to allow |
| */ |
| public void allowProperty(String name) |
| { |
| accessedKeys.add(name); |
| } |
| |
| /** |
| * Gets the value for the given property name. Also records that this |
| * property was accessed. |
| * |
| * @param name the property name |
| * @return the value for the property, or null if property does not exist |
| * in this map. |
| */ |
| public Object get(Object name) |
| { |
| accessedKeys.add(name); |
| return super.get(name); |
| } |
| |
| /** |
| * Helper method to get the property with the specified name as a string if possible. |
| * |
| * @param name The property name. |
| * @return The property object. |
| */ |
| private Object getSinglePropertyOrFail(Object name) |
| { |
| Object result = get(name); |
| if (result instanceof ValueList) |
| { |
| ConfigurationException exception = new ConfigurationException(); |
| exception.setMessage |
| (UNEXPECTED_MULTIPLE_VALUES, new Object[] {name}); |
| throw exception; |
| } |
| return result; |
| } |
| |
| /** |
| * Gets the property with the specified name as a string if possible. |
| * |
| * @param name The property name. |
| * @return The property name. |
| */ |
| public String getProperty(String name) |
| { |
| return getPropertyAsString(name, null); |
| } |
| |
| /** |
| * Gets the property with the specified name as a ConfigMap if possible, |
| * or returns the default value if the property is undefined. |
| * |
| * @param name the property name |
| * @param defaultValue the default value |
| * @return ConfigMap the ConfigMap object of the property |
| */ |
| public ConfigMap getPropertyAsMap(String name, ConfigMap defaultValue) |
| { |
| Object prop = getSinglePropertyOrFail(name); |
| if (prop instanceof ConfigMap) |
| { |
| return (ConfigMap)prop; |
| } |
| return defaultValue; |
| } |
| |
| /** |
| * Gets the property with the specified name as a String if possible, |
| * or returns the default value if the property is undefined. |
| * |
| * @param name the property name |
| * @param defaultValue the default value |
| * @return String the String value of the property |
| */ |
| public String getPropertyAsString(String name, String defaultValue) |
| { |
| Object prop = getSinglePropertyOrFail(name); |
| if (prop instanceof String) |
| { |
| return (String)prop; |
| } |
| return defaultValue; |
| } |
| |
| /** |
| * Gets a property (or set of properties) as a List. If only one |
| * property exists it is added as the only entry to a new List. |
| * |
| * @param name the property name |
| * @param defaultValue the value to return if the property is not found |
| * @return the value for the property as a List if it exists in this map, |
| * otherwise the defaultValue is returned. |
| */ |
| public List getPropertyAsList(String name, List defaultValue) |
| { |
| Object prop = get(name); |
| if (prop != null) |
| { |
| if (prop instanceof List) |
| { |
| return (List) prop; |
| } |
| else |
| { |
| List list = new ArrayList(); |
| list.add(prop); |
| return list; |
| } |
| } |
| return defaultValue; |
| } |
| |
| /** |
| * Gets the property with the specified name as a boolean if possible, |
| * or returns the default value if the property is undefined. |
| * |
| * @param name the property name |
| * @param defaultValue the default value |
| * @return boolean the boolean value of the property |
| */ |
| public boolean getPropertyAsBoolean(String name, boolean defaultValue) |
| { |
| Object prop = getSinglePropertyOrFail(name); |
| if (prop instanceof String) |
| { |
| return Boolean.valueOf((String)prop).booleanValue(); |
| } |
| return defaultValue; |
| } |
| |
| /** |
| * Gets the property with the specified name as an int if possible, |
| * or returns the default value if the property is undefined. |
| * |
| * @param name the property name |
| * @param defaultValue the default value |
| * @return int the int value of the property |
| */ |
| public int getPropertyAsInt(String name, int defaultValue) |
| { |
| Object prop = getSinglePropertyOrFail(name); |
| if (prop instanceof String) |
| { |
| try |
| { |
| return Integer.parseInt((String)prop); |
| } |
| catch (NumberFormatException ex) |
| { |
| } |
| } |
| return defaultValue; |
| } |
| |
| /** |
| * Gets the property with the specified name as a long if possible, |
| * or returns the default value if the property is undefined. |
| * |
| * @param name the property name |
| * @param defaultValue the default value |
| * @return long the long value of the property |
| */ |
| public long getPropertyAsLong(String name, long defaultValue) |
| { |
| Object prop = getSinglePropertyOrFail(name); |
| if (prop instanceof String) |
| { |
| try |
| { |
| return Long.parseLong((String)prop); |
| } |
| catch (NumberFormatException ex) |
| { |
| } |
| } |
| return defaultValue; |
| } |
| |
| /** |
| * Returns a list of qualified property names that have not been accessed |
| * by one of the get*() methods. |
| * |
| * @return List a list of unused properties |
| */ |
| public List findAllUnusedProperties() |
| { |
| List result = new ArrayList(); |
| findUnusedProperties("", true, result); |
| return result; |
| } |
| |
| /** |
| * Gathers a collection of properties that exist in the map but have not |
| * been explicitly accessed nor marked as allowed. This list is helpful |
| * in validating a set of properties as one can detect those that are |
| * unknown or unexpected. |
| * |
| * @param parentPath Used to track the depth of property in a potential |
| * hierarchy of <code>ConfigMap</code>s. |
| * @param recurse Whether sub maps should be recursively searched. |
| * @param result the collection of unused properties in this map. |
| */ |
| public void findUnusedProperties |
| (String parentPath, boolean recurse, Collection result) |
| { |
| Iterator itr = entrySet().iterator(); |
| while (itr.hasNext()) |
| { |
| Map.Entry entry = (Map.Entry) itr.next(); |
| Object key = entry.getKey(); |
| String currentPath = parentPath + '/' + String.valueOf(key); |
| if (!accessedKeys.contains(key)) |
| { |
| result.add(currentPath); |
| } |
| else if (recurse) |
| { |
| Object value = entry.getValue(); |
| List values = value instanceof List ? |
| (List) value : Collections.singletonList(value); |
| for (int i = 0; i < values.size(); i++) |
| { |
| Object child = values.get(i); |
| if (child instanceof ConfigMap) |
| { |
| ((ConfigMap) child).findUnusedProperties |
| (currentPath, recurse, result); |
| } |
| } |
| } |
| } |
| } |
| } |