/* * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ package javax.management; import com.sun.jmx.mbeanserver.GetPropertyAction; import com.sun.jmx.mbeanserver.Util; import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; import java.security.AccessController; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; /** *

Represents the object name of an MBean, or a pattern that can * match the names of several MBeans. Instances of this class are * immutable.

* *

An instance of this class can be used to represent:

* * *

An object name consists of two parts, the domain and the key * properties.

* *

The domain is a string of characters not including * the character colon (:). It is recommended that the domain * should not contain the string "{@code //}", which is reserved for future use. * *

If the domain includes at least one occurrence of the wildcard * characters asterisk (*) or question mark * (?), then the object name is a pattern. The asterisk * matches any sequence of zero or more characters, while the question * mark matches any single character.

* *

If the domain is empty, it will be replaced in certain contexts * by the default domain of the MBean server in which the * ObjectName is used.

* *

The key properties are an unordered set of keys and * associated values.

* *

Each key is a nonempty string of characters which may * not contain any of the characters comma (,), equals * (=), colon, asterisk, or question mark. The same key * may not occur twice in a given ObjectName.

* *

Each value associated with a key is a string of * characters that is either unquoted or quoted.

* *

An unquoted value is a possibly empty string of * characters which may not contain any of the characters comma, * equals, colon, or quote.

* *

If the unquoted value contains at least one occurrence * of the wildcard characters asterisk or question mark, then the object * name is a property value pattern. The asterisk matches any * sequence of zero or more characters, while the question mark matches * any single character.

* *

A quoted value consists of a quote ("), * followed by a possibly empty string of characters, followed by * another quote. Within the string of characters, the backslash * (\) has a special meaning. It must be followed by * one of the following characters:

* * * *

A quote may not appear inside a quoted value except immediately * after an odd number of consecutive backslashes.

* *

The quotes surrounding a quoted value, and any backslashes * within that value, are considered to be part of the value.

* *

If the quoted value contains at least one occurrence of * the characters asterisk or question mark and they are not preceded * by a backslash, then they are considered as wildcard characters and * the object name is a property value pattern. The asterisk * matches any sequence of zero or more characters, while the question * mark matches any single character.

* *

An ObjectName may be a property list pattern. In this * case it may have zero or more keys and associated values. It matches * a nonpattern ObjectName whose domain matches and that contains the * same keys and associated values, as well as possibly other keys and * values.

* *

An ObjectName is a property value pattern when at least * one of its quoted or unquoted key property values * contains the wildcard characters asterisk or question mark as described * above. In this case it has one or more keys and associated values, with * at least one of the values containing wildcard characters. It matches a * nonpattern ObjectName whose domain matches and that contains the same * keys whose values match; if the property value pattern is also a * property list pattern then the nonpattern ObjectName can contain * other keys and values.

* *

An ObjectName is a property pattern if it is either a * property list pattern or a property value pattern * or both.

* *

An ObjectName is a pattern if its domain contains a wildcard or * if the ObjectName is a property pattern.

* *

If an ObjectName is not a pattern, it must contain at least one * key with its associated value.

* *

Examples of ObjectName patterns are:

* * * *

An ObjectName can be written as a String with the following * elements in order:

* * * *

A key property list written as a String is a comma-separated * list of elements. Each element is either an asterisk or a key * property. A key property consists of a key, an equals * (=), and the associated value.

* *

At most one element of a key property list may be an asterisk. * If the key property list contains an asterisk element, the * ObjectName is a property list pattern.

* *

Spaces have no special significance in a String representing an * ObjectName. For example, the String: *

 * domain: key1 = value1 , key2 = value2
 * 
* represents an ObjectName with two keys. The name of each key * contains six characters, of which the first and last are spaces. * The value associated with the key " key1 " * also begins and ends with a space.

* *

In addition to the restrictions on characters spelt out above, * no part of an ObjectName may contain a newline character * ('\n'), whether the domain, a key, or a value, whether * quoted or unquoted. The newline character can be represented in a * quoted value with the sequence \n. * *

The rules on special characters and quoting apply regardless of * which constructor is used to make an ObjectName.

* *

To avoid collisions between MBeans supplied by different * vendors, a useful convention is to begin the domain name with the * reverse DNS name of the organization that specifies the MBeans, * followed by a period and a string whose interpretation is * determined by that organization. For example, MBeans specified by * Sun Microsystems Inc., DNS name sun.com, would have * domains such as com.sun.MyDomain. This is essentially * the same convention as for Java-language package names.

* *

The serialVersionUID of this class is 1081892073854801359L. * * @since 1.5 */ @SuppressWarnings("serial") // don't complain serialVersionUID not constant public class ObjectName implements Comparable, QueryExp { /** * A structure recording property structure and * proposing minimal services */ private static class Property { int _key_index; int _key_length; int _value_length; /** * Constructor. */ Property(int key_index, int key_length, int value_length) { _key_index = key_index; _key_length = key_length; _value_length = value_length; } /** * Assigns the key index of property */ void setKeyIndex(int key_index) { _key_index = key_index; } /** * Returns a key string for receiver key */ String getKeyString(String name) { return name.substring(_key_index, _key_index + _key_length); } /** * Returns a value string for receiver key */ String getValueString(String name) { int in_begin = _key_index + _key_length + 1; int out_end = in_begin + _value_length; return name.substring(in_begin, out_end); } } /** * Marker class for value pattern property. */ private static class PatternProperty extends Property { /** * Constructor. */ PatternProperty(int key_index, int key_length, int value_length) { super(key_index, key_length, value_length); } } // Inner classes <======================================== // Private fields ----------------------------------------> // Serialization compatibility stuff --------------------> // Two serial forms are supported in this class. The selected form depends // on system property "jmx.serial.form": // - "1.0" for JMX 1.0 // - any other value for JMX 1.1 and higher // // Serial version for old serial form private static final long oldSerialVersionUID = -5467795090068647408L; // // Serial version for new serial form private static final long newSerialVersionUID = 1081892073854801359L; // // Serializable fields in old serial form private static final ObjectStreamField[] oldSerialPersistentFields = { new ObjectStreamField("domain", String.class), new ObjectStreamField("propertyList", Hashtable.class), new ObjectStreamField("propertyListString", String.class), new ObjectStreamField("canonicalName", String.class), new ObjectStreamField("pattern", Boolean.TYPE), new ObjectStreamField("propertyPattern", Boolean.TYPE) }; // // Serializable fields in new serial form private static final ObjectStreamField[] newSerialPersistentFields = { }; // // Actual serial version and serial form private static final long serialVersionUID; private static final ObjectStreamField[] serialPersistentFields; private static boolean compat = false; static { try { GetPropertyAction act = new GetPropertyAction("jmx.serial.form"); String form = AccessController.doPrivileged(act); compat = (form != null && form.equals("1.0")); } catch (Exception e) { // OK: exception means no compat with 1.0, too bad } if (compat) { serialPersistentFields = oldSerialPersistentFields; serialVersionUID = oldSerialVersionUID; } else { serialPersistentFields = newSerialPersistentFields; serialVersionUID = newSerialVersionUID; } } // // Serialization compatibility stuff <============================== // Class private fields -----------------------------------> /** * a shared empty array for empty property lists */ static final private Property[] _Empty_property_array = new Property[0]; // Class private fields <============================== // Instance private fields -----------------------------------> /** * a String containing the canonical name */ private transient String _canonicalName; /** * An array of properties in the same seq order as time creation */ private transient Property[] _kp_array; /** * An array of properties in the same seq order as canonical order */ private transient Property[] _ca_array; /** * The length of the domain part of built objectname */ private transient int _domain_length = 0; /** * The propertyList of built object name. Initialized lazily. * Table that contains all the pairs (key,value) for this ObjectName. */ private transient Map _propertyList; /** * boolean that declares if this ObjectName domain part is a pattern */ private transient boolean _domain_pattern = false; /** * boolean that declares if this ObjectName contains a pattern on the * key property list */ private transient boolean _property_list_pattern = false; /** * boolean that declares if this ObjectName contains a pattern on the * value of at least one key property */ private transient boolean _property_value_pattern = false; // Instance private fields <======================================= // Private fields <======================================== // Private methods ----------------------------------------> // Category : Instance construction -------------------------> /** * Initializes this {@link ObjectName} from the given string * representation. * * @param name A string representation of the {@link ObjectName} * * @exception MalformedObjectNameException The string passed as a * parameter does not have the right format. * @exception NullPointerException The name parameter * is null. */ private void construct(String name) throws MalformedObjectNameException, NullPointerException { // The name cannot be null if (name == null) throw new NullPointerException("name cannot be null"); // Test if the name is empty if (name.length() == 0) { // this is equivalent to the whole word query object name. _canonicalName = "*:*"; _kp_array = _Empty_property_array; _ca_array = _Empty_property_array; _domain_length = 1; _propertyList = null; _domain_pattern = true; _property_list_pattern = true; _property_value_pattern = false; return; } // initialize parsing of the string char[] name_chars = name.toCharArray(); int len = name_chars.length; char[] canonical_chars = new char[len]; // canonical form will be same // length at most int cname_index = 0; int index = 0; char c, c1; // parses domain part domain_parsing: while (index < len) { switch (name_chars[index]) { case ':' : _domain_length = index++; break domain_parsing; case '=' : // ":" omission check. // // Although "=" is a valid character in the domain part // it is true that it is rarely used in the real world. // So check straight away if the ":" has been omitted // from the ObjectName. This allows us to provide a more // accurate exception message. int i = ++index; while ((i < len) && (name_chars[i++] != ':')) if (i == len) throw new MalformedObjectNameException( "Domain part must be specified"); break; case '\n' : throw new MalformedObjectNameException( "Invalid character '\\n' in domain name"); case '*' : case '?' : _domain_pattern = true; index++; break; default : index++; break; } } // check for non-empty properties if (index == len) throw new MalformedObjectNameException( "Key properties cannot be empty"); // we have got the domain part, begins building of _canonicalName System.arraycopy(name_chars, 0, canonical_chars, 0, _domain_length); canonical_chars[_domain_length] = ':'; cname_index = _domain_length + 1; // parses property list Property prop; Map keys_map = new HashMap(); String[] keys; String key_name; boolean quoted_value; int property_index = 0; int in_index; int key_index, key_length, value_index, value_length; keys = new String[10]; _kp_array = new Property[10]; _property_list_pattern = false; _property_value_pattern = false; while (index < len) { c = name_chars[index]; // case of pattern properties if (c == '*') { if (_property_list_pattern) throw new MalformedObjectNameException( "Cannot have several '*' characters in pattern " + "property list"); else { _property_list_pattern = true; if ((++index < len ) && (name_chars[index] != ',')) throw new MalformedObjectNameException( "Invalid character found after '*': end of " + "name or ',' expected"); else if (index == len) { if (property_index == 0) { // empty properties case _kp_array = _Empty_property_array; _ca_array = _Empty_property_array; _propertyList = Collections.emptyMap(); } break; } else { // correct pattern spec in props, continue index++; continue; } } } // standard property case, key part in_index = index; key_index = in_index; if (name_chars[in_index] == '=') throw new MalformedObjectNameException("Invalid key (empty)"); while ((in_index < len) && ((c1 = name_chars[in_index++]) != '=')) switch (c1) { // '=' considered to introduce value part case '*' : case '?' : case ',' : case ':' : case '\n' : final String ichar = ((c1=='\n')?"\\n":""+c1); throw new MalformedObjectNameException( "Invalid character '" + ichar + "' in key part of property"); } if (name_chars[in_index - 1] != '=') throw new MalformedObjectNameException( "Unterminated key property part"); value_index = in_index; // in_index pointing after '=' char key_length = value_index - key_index - 1; // found end of key // standard property case, value part boolean value_pattern = false; if (in_index < len && name_chars[in_index] == '\"') { quoted_value = true; // the case of quoted value part quoted_value_parsing: while ((++in_index < len) && ((c1 = name_chars[in_index]) != '\"')) { // the case of an escaped character if (c1 == '\\') { if (++in_index == len) throw new MalformedObjectNameException( "Unterminated quoted value"); switch (c1 = name_chars[in_index]) { case '\\' : case '\"' : case '?' : case '*' : case 'n' : break; // valid character default : throw new MalformedObjectNameException( "Invalid escape sequence '\\" + c1 + "' in quoted value"); } } else if (c1 == '\n') { throw new MalformedObjectNameException( "Newline in quoted value"); } else { switch (c1) { case '?' : case '*' : value_pattern = true; break; } } } if (in_index == len) throw new MalformedObjectNameException( "Unterminated quoted value"); else value_length = ++in_index - value_index; } else { // the case of standard value part quoted_value = false; while ((in_index < len) && ((c1 = name_chars[in_index]) != ',')) switch (c1) { // ',' considered to be the value separator case '*' : case '?' : value_pattern = true; in_index++; break; case '=' : case ':' : case '"' : case '\n' : final String ichar = ((c1=='\n')?"\\n":""+c1); throw new MalformedObjectNameException( "Invalid character '" + ichar + "' in value part of property"); default : in_index++; break; } value_length = in_index - value_index; } // Parsed property, checks the end of name if (in_index == len - 1) { if (quoted_value) throw new MalformedObjectNameException( "Invalid ending character `" + name_chars[in_index] + "'"); else throw new MalformedObjectNameException( "Invalid ending comma"); } else in_index++; // we got the key and value part, prepare a property for this if (!value_pattern) { prop = new Property(key_index, key_length, value_length); } else { _property_value_pattern = true; prop = new PatternProperty(key_index, key_length, value_length); } key_name = name.substring(key_index, key_index + key_length); if (property_index == keys.length) { String[] tmp_string_array = new String[property_index + 10]; System.arraycopy(keys, 0, tmp_string_array, 0, property_index); keys = tmp_string_array; } keys[property_index] = key_name; addProperty(prop, property_index, keys_map, key_name); property_index++; index = in_index; } // computes and set canonical name setCanonicalName(name_chars, canonical_chars, keys, keys_map, cname_index, property_index); } /** * Construct an ObjectName from a domain and a Hashtable. * * @param domain Domain of the ObjectName. * @param props Map containing couples key -> value. * * @exception MalformedObjectNameException The domain * contains an illegal character, or one of the keys or values in * table contains an illegal character, or one of the * values in table does not follow the rules for quoting. * @exception NullPointerException One of the parameters is null. */ private void construct(String domain, Map props) throws MalformedObjectNameException, NullPointerException { // The domain cannot be null if (domain == null) throw new NullPointerException("domain cannot be null"); // The key property list cannot be null if (props == null) throw new NullPointerException("key property list cannot be null"); // The key property list cannot be empty if (props.isEmpty()) throw new MalformedObjectNameException( "key property list cannot be empty"); // checks domain validity if (!isDomain(domain)) throw new MalformedObjectNameException("Invalid domain: " + domain); // init canonicalname final StringBuilder sb = new StringBuilder(); sb.append(domain).append(':'); _domain_length = domain.length(); // allocates the property array int nb_props = props.size(); _kp_array = new Property[nb_props]; String[] keys = new String[nb_props]; final Map keys_map = new HashMap(); Property prop; int key_index; int i = 0; for (Map.Entry entry : props.entrySet()) { if (sb.length() > 0) sb.append(","); String key = entry.getKey(); String value; try { value = entry.getValue(); } catch (ClassCastException e) { throw new MalformedObjectNameException(e.getMessage()); } key_index = sb.length(); checkKey(key); sb.append(key); keys[i] = key; sb.append("="); boolean value_pattern = checkValue(value); sb.append(value); if (!value_pattern) { prop = new Property(key_index, key.length(), value.length()); } else { _property_value_pattern = true; prop = new PatternProperty(key_index, key.length(), value.length()); } addProperty(prop, i, keys_map, key); i++; } // initialize canonical name and data structure int len = sb.length(); char[] initial_chars = new char[len]; sb.getChars(0, len, initial_chars, 0); char[] canonical_chars = new char[len]; System.arraycopy(initial_chars, 0, canonical_chars, 0, _domain_length + 1); setCanonicalName(initial_chars, canonical_chars, keys, keys_map, _domain_length + 1, _kp_array.length); } // Category : Instance construction <============================== // Category : Internal utilities ------------------------------> /** * Add passed property to the list at the given index * for the passed key name */ private void addProperty(Property prop, int index, Map keys_map, String key_name) throws MalformedObjectNameException { if (keys_map.containsKey(key_name)) throw new MalformedObjectNameException("key `" + key_name +"' already defined"); // if no more space for property arrays, have to increase it if (index == _kp_array.length) { Property[] tmp_prop_array = new Property[index + 10]; System.arraycopy(_kp_array, 0, tmp_prop_array, 0, index); _kp_array = tmp_prop_array; } _kp_array[index] = prop; keys_map.put(key_name, prop); } /** * Sets the canonical name of receiver from input 'specified_chars' * array, by filling 'canonical_chars' array with found 'nb-props' * properties starting at position 'prop_index'. */ private void setCanonicalName(char[] specified_chars, char[] canonical_chars, String[] keys, Map keys_map, int prop_index, int nb_props) { // Sort the list of found properties if (_kp_array != _Empty_property_array) { String[] tmp_keys = new String[nb_props]; Property[] tmp_props = new Property[nb_props]; System.arraycopy(keys, 0, tmp_keys, 0, nb_props); Arrays.sort(tmp_keys); keys = tmp_keys; System.arraycopy(_kp_array, 0, tmp_props, 0 , nb_props); _kp_array = tmp_props; _ca_array = new Property[nb_props]; // now assigns _ca_array to the sorted list of keys // (there cannot be two identical keys in an objectname. for (int i = 0; i < nb_props; i++) _ca_array[i] = keys_map.get(keys[i]); // now we build the canonical name and set begin indexes of // properties to reflect canonical form int last_index = nb_props - 1; int prop_len; Property prop; for (int i = 0; i <= last_index; i++) { prop = _ca_array[i]; // length of prop including '=' char prop_len = prop._key_length + prop._value_length + 1; System.arraycopy(specified_chars, prop._key_index, canonical_chars, prop_index, prop_len); prop.setKeyIndex(prop_index); prop_index += prop_len; if (i != last_index) { canonical_chars[prop_index] = ','; prop_index++; } } } // terminate canonicalname with '*' in case of pattern if (_property_list_pattern) { if (_kp_array != _Empty_property_array) canonical_chars[prop_index++] = ','; canonical_chars[prop_index++] = '*'; } // we now build the canonicalname string _canonicalName = (new String(canonical_chars, 0, prop_index)).intern(); } /** * Parse a key. *

final int endKey=parseKey(s,startKey);
*

key starts at startKey (included), and ends at endKey (excluded). * If (startKey == endKey), then the key is empty. * * @param s The char array of the original string. * @param startKey index at which to begin parsing. * @return The index following the last character of the key. **/ private static int parseKey(final char[] s, final int startKey) throws MalformedObjectNameException { int next = startKey; int endKey = startKey; final int len = s.length; while (next < len) { final char k = s[next++]; switch (k) { case '*': case '?': case ',': case ':': case '\n': final String ichar = ((k=='\n')?"\\n":""+k); throw new MalformedObjectNameException("Invalid character in key: `" + ichar + "'"); case '=': // we got the key. endKey = next-1; break; default: if (next < len) continue; else endKey=next; } break; } return endKey; } /** * Parse a value. *

final int endVal=parseValue(s,startVal);
*

value starts at startVal (included), and ends at endVal (excluded). * If (startVal == endVal), then the key is empty. * * @param s The char array of the original string. * @param startValue index at which to begin parsing. * @return The first element of the int array indicates the index * following the last character of the value. The second * element of the int array indicates that the value is * a pattern when its value equals 1. **/ private static int[] parseValue(final char[] s, final int startValue) throws MalformedObjectNameException { boolean value_pattern = false; int next = startValue; int endValue = startValue; final int len = s.length; final char q=s[startValue]; if (q == '"') { // quoted value if (++next == len) throw new MalformedObjectNameException("Invalid quote"); while (next < len) { char last = s[next]; if (last == '\\') { if (++next == len) throw new MalformedObjectNameException( "Invalid unterminated quoted character sequence"); last = s[next]; switch (last) { case '\\' : case '?' : case '*' : case 'n' : break; case '\"' : // We have an escaped quote. If this escaped // quote is the last character, it does not // qualify as a valid termination quote. // if (next+1 == len) throw new MalformedObjectNameException( "Missing termination quote"); break; default: throw new MalformedObjectNameException( "Invalid quoted character sequence '\\" + last + "'"); } } else if (last == '\n') { throw new MalformedObjectNameException( "Newline in quoted value"); } else if (last == '\"') { next++; break; } else { switch (last) { case '?' : case '*' : value_pattern = true; break; } } next++; // Check that last character is a termination quote. // We have already handled the case were the last // character is an escaped quote earlier. // if ((next >= len) && (last != '\"')) throw new MalformedObjectNameException("Missing termination quote"); } endValue = next; if (next < len) { if (s[next++] != ',') throw new MalformedObjectNameException("Invalid quote"); } } else { // Non quoted value. while (next < len) { final char v=s[next++]; switch(v) { case '*': case '?': value_pattern = true; if (next < len) continue; else endValue=next; break; case '=': case ':': case '\n' : final String ichar = ((v=='\n')?"\\n":""+v); throw new MalformedObjectNameException("Invalid character `" + ichar + "' in value"); case ',': endValue = next-1; break; default: if (next < len) continue; else endValue=next; } break; } } return new int[] { endValue, value_pattern ? 1 : 0 }; } /** * Check if the supplied value is a valid value. * * @return true if the value is a pattern, otherwise false. */ private static boolean checkValue(String val) throws MalformedObjectNameException { if (val == null) throw new NullPointerException("Invalid value (null)"); final int len = val.length(); if (len == 0) return false; final char[] s = val.toCharArray(); final int[] result = parseValue(s,0); final int endValue = result[0]; final boolean value_pattern = result[1] == 1; if (endValue < len) throw new MalformedObjectNameException("Invalid character in value: `" + s[endValue] + "'"); return value_pattern; } /** * Check if the supplied key is a valid key. */ private static void checkKey(String key) throws MalformedObjectNameException, NullPointerException { if (key == null) throw new NullPointerException("Invalid key (null)"); final int len = key.length(); if (len == 0) throw new MalformedObjectNameException("Invalid key (empty)"); final char[] k=key.toCharArray(); final int endKey = parseKey(k,0); if (endKey < len) throw new MalformedObjectNameException("Invalid character in value: `" + k[endKey] + "'"); } /* * Tests whether string s is matched by pattern p. * Supports "?", "*" each of which may be escaped with "\"; * Not yet supported: internationalization; "\" inside brackets.

* Wildcard matching routine by Karl Heuer. Public Domain.

*/ private static boolean wildmatch(char[] s, char[] p, int si, int pi) { char c; final int slen = s.length; final int plen = p.length; while (pi < plen) { // While still string c = p[pi++]; if (c == '?') { if (++si > slen) return false; } else if (c == '*') { // Wildcard if (pi >= plen) return true; do { if (wildmatch(s,p,si,pi)) return true; } while (++si < slen); return false; } else { if (si >= slen || c != s[si++]) return false; } } return (si == slen); } // Category : Internal utilities <============================== // Category : Internal accessors ------------------------------> /** * Check if domain is a valid domain. Set _domain_pattern if appropriate. */ private boolean isDomain(String domain) { if (domain == null) return true; final char[] d=domain.toCharArray(); final int len = d.length; int next = 0; while (next < len) { final char c = d[next++]; switch (c) { case ':' : case '\n' : return false; case '*' : case '?' : _domain_pattern = true; break; } } return true; } // Category : Internal accessors <============================== // Category : Serialization -----------------------------------> /** * Deserializes an {@link ObjectName} from an {@link ObjectInputStream}. * @serialData

*/ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { String cn; if (compat) { // Read an object serialized in the old serial form // //in.defaultReadObject(); final ObjectInputStream.GetField fields = in.readFields(); String propListString = (String)fields.get("propertyListString", ""); // 6616825: take care of property patterns final boolean propPattern = fields.get("propertyPattern" , false); if (propPattern) { propListString = (propListString.length()==0?"*":(propListString+",*")); } cn = (String)fields.get("domain", "default")+ ":"+ propListString; } else { // Read an object serialized in the new serial form // in.defaultReadObject(); cn = (String)in.readObject(); } try { construct(cn); } catch (NullPointerException e) { throw new InvalidObjectException(e.toString()); } catch (MalformedObjectNameException e) { throw new InvalidObjectException(e.toString()); } } /** * Serializes an {@link ObjectName} to an {@link ObjectOutputStream}. * @serialData */ private void writeObject(ObjectOutputStream out) throws IOException { if (compat) { // Serializes this instance in the old serial form // Read CR 6441274 before making any changes to this code ObjectOutputStream.PutField fields = out.putFields(); fields.put("domain", _canonicalName.substring(0, _domain_length)); fields.put("propertyList", getKeyPropertyList()); fields.put("propertyListString", getKeyPropertyListString()); fields.put("canonicalName", _canonicalName); fields.put("pattern", (_domain_pattern || _property_list_pattern)); fields.put("propertyPattern", _property_list_pattern); out.writeFields(); } else { // Serializes this instance in the new serial form // out.defaultWriteObject(); out.writeObject(getSerializedNameString()); } } // Category : Serialization <=================================== // Private methods <======================================== // Public methods ----------------------------------------> // Category : ObjectName Construction ------------------------------> /** *

Return an instance of ObjectName that can be used anywhere * an object obtained with {@link #ObjectName(String) new * ObjectName(name)} can be used. The returned object may be of * a subclass of ObjectName. Calling this method twice with the * same parameters may return the same object or two equal but * not identical objects.

* * @param name A string representation of the object name. * * @return an ObjectName corresponding to the given String. * * @exception MalformedObjectNameException The string passed as a * parameter does not have the right format. * @exception NullPointerException The name parameter * is null. * */ public static ObjectName getInstance(String name) throws MalformedObjectNameException, NullPointerException { return new ObjectName(name); } /** *

Return an instance of ObjectName that can be used anywhere * an object obtained with {@link #ObjectName(String, String, * String) new ObjectName(domain, key, value)} can be used. The * returned object may be of a subclass of ObjectName. Calling * this method twice with the same parameters may return the same * object or two equal but not identical objects.

* * @param domain The domain part of the object name. * @param key The attribute in the key property of the object name. * @param value The value in the key property of the object name. * * @return an ObjectName corresponding to the given domain, * key, and value. * * @exception MalformedObjectNameException The * domain, key, or value * contains an illegal character, or value does not * follow the rules for quoting. * @exception NullPointerException One of the parameters is null. * */ public static ObjectName getInstance(String domain, String key, String value) throws MalformedObjectNameException, NullPointerException { return new ObjectName(domain, key, value); } /** *

Return an instance of ObjectName that can be used anywhere * an object obtained with {@link #ObjectName(String, Hashtable) * new ObjectName(domain, table)} can be used. The returned * object may be of a subclass of ObjectName. Calling this method * twice with the same parameters may return the same object or * two equal but not identical objects.

* * @param domain The domain part of the object name. * @param table A hash table containing one or more key * properties. The key of each entry in the table is the key of a * key property in the object name. The associated value in the * table is the associated value in the object name. * * @return an ObjectName corresponding to the given domain and * key mappings. * * @exception MalformedObjectNameException The domain * contains an illegal character, or one of the keys or values in * table contains an illegal character, or one of the * values in table does not follow the rules for * quoting. * @exception NullPointerException One of the parameters is null. * */ public static ObjectName getInstance(String domain, Hashtable table) throws MalformedObjectNameException, NullPointerException { return new ObjectName(domain, table); } /** *

Return an instance of ObjectName that can be used anywhere * the given object can be used. The returned object may be of a * subclass of ObjectName. If name is of a subclass * of ObjectName, it is not guaranteed that the returned object * will be of the same class.

* *

The returned value may or may not be identical to * name. Calling this method twice with the same * parameters may return the same object or two equal but not * identical objects.

* *

Since ObjectName is immutable, it is not usually useful to * make a copy of an ObjectName. The principal use of this method * is to guard against a malicious caller who might pass an * instance of a subclass with surprising behavior to sensitive * code. Such code can call this method to obtain an ObjectName * that is known not to have surprising behavior.

* * @param name an instance of the ObjectName class or of a subclass * * @return an instance of ObjectName or a subclass that is known to * have the same semantics. If name respects the * semantics of ObjectName, then the returned object is equal * (though not necessarily identical) to name. * * @exception NullPointerException The name is null. * */ public static ObjectName getInstance(ObjectName name) throws NullPointerException { if (name.getClass().equals(ObjectName.class)) return name; return Util.newObjectName(name.getSerializedNameString()); } /** * Construct an object name from the given string. * * @param name A string representation of the object name. * * @exception MalformedObjectNameException The string passed as a * parameter does not have the right format. * @exception NullPointerException The name parameter * is null. */ public ObjectName(String name) throws MalformedObjectNameException, NullPointerException { construct(name); } /** * Construct an object name with exactly one key property. * * @param domain The domain part of the object name. * @param key The attribute in the key property of the object name. * @param value The value in the key property of the object name. * * @exception MalformedObjectNameException The * domain, key, or value * contains an illegal character, or value does not * follow the rules for quoting. * @exception NullPointerException One of the parameters is null. */ public ObjectName(String domain, String key, String value) throws MalformedObjectNameException, NullPointerException { // If key or value are null a NullPointerException // will be thrown by the put method in Hashtable. // Map table = Collections.singletonMap(key, value); construct(domain, table); } /** * Construct an object name with several key properties from a Hashtable. * * @param domain The domain part of the object name. * @param table A hash table containing one or more key * properties. The key of each entry in the table is the key of a * key property in the object name. The associated value in the * table is the associated value in the object name. * * @exception MalformedObjectNameException The domain * contains an illegal character, or one of the keys or values in * table contains an illegal character, or one of the * values in table does not follow the rules for * quoting. * @exception NullPointerException One of the parameters is null. */ public ObjectName(String domain, Hashtable table) throws MalformedObjectNameException, NullPointerException { construct(domain, table); /* The exception for when a key or value in the table is not a String is now ClassCastException rather than MalformedObjectNameException. This was not previously specified. */ } // Category : ObjectName Construction <============================== // Category : Getter methods ------------------------------> /** * Checks whether the object name is a pattern. *

* An object name is a pattern if its domain contains a * wildcard or if the object name is a property pattern. * * @return True if the name is a pattern, otherwise false. */ public boolean isPattern() { return (_domain_pattern || _property_list_pattern || _property_value_pattern); } /** * Checks whether the object name is a pattern on the domain part. * * @return True if the name is a domain pattern, otherwise false. * */ public boolean isDomainPattern() { return _domain_pattern; } /** * Checks whether the object name is a pattern on the key properties. *

* An object name is a pattern on the key properties if it is a * pattern on the key property list (e.g. "d:k=v,*") or on the * property values (e.g. "d:k=*") or on both (e.g. "d:k=*,*"). * * @return True if the name is a property pattern, otherwise false. */ public boolean isPropertyPattern() { return _property_list_pattern || _property_value_pattern; } /** * Checks whether the object name is a pattern on the key property list. *

* For example, "d:k=v,*" and "d:k=*,*" are key property list patterns * whereas "d:k=*" is not. * * @return True if the name is a property list pattern, otherwise false. * * @since 1.6 */ public boolean isPropertyListPattern() { return _property_list_pattern; } /** * Checks whether the object name is a pattern on the value part * of at least one of the key properties. *

* For example, "d:k=*" and "d:k=*,*" are property value patterns * whereas "d:k=v,*" is not. * * @return True if the name is a property value pattern, otherwise false. * * @since 1.6 */ public boolean isPropertyValuePattern() { return _property_value_pattern; } /** * Checks whether the value associated with a key in a key * property is a pattern. * * @param property The property whose value is to be checked. * * @return True if the value associated with the given key property * is a pattern, otherwise false. * * @exception NullPointerException If property is null. * @exception IllegalArgumentException If property is not * a valid key property for this ObjectName. * * @since 1.6 */ public boolean isPropertyValuePattern(String property) throws NullPointerException, IllegalArgumentException { if (property == null) throw new NullPointerException("key property can't be null"); for (int i = 0; i < _ca_array.length; i++) { Property prop = _ca_array[i]; String key = prop.getKeyString(_canonicalName); if (key.equals(property)) return (prop instanceof PatternProperty); } throw new IllegalArgumentException("key property not found"); } /** *

Returns the canonical form of the name; that is, a string * representation where the properties are sorted in lexical * order.

* *

More precisely, the canonical form of the name is a String * consisting of the domain part, a colon * (:), the canonical key property list, and * a pattern indication.

* *

The canonical key property list is the same string * as described for {@link #getCanonicalKeyPropertyListString()}.

* *

The pattern indication is: *

    *
  • empty for an ObjectName * that is not a property list pattern; *
  • an asterisk for an ObjectName * that is a property list pattern with no keys; or *
  • a comma and an * asterisk (,*) for an ObjectName that is a property * list pattern with at least one key. *

* * @return The canonical form of the name. */ public String getCanonicalName() { return _canonicalName; } /** * Returns the domain part. * * @return The domain. */ public String getDomain() { return _canonicalName.substring(0, _domain_length); } /** * Obtains the value associated with a key in a key property. * * @param property The property whose value is to be obtained. * * @return The value of the property, or null if there is no such * property in this ObjectName. * * @exception NullPointerException If property is null. */ public String getKeyProperty(String property) throws NullPointerException { return _getKeyPropertyList().get(property); } /** *

Returns the key properties as a Map. The returned * value is a Map in which each key is a key in the * ObjectName's key property list and each value is the associated * value.

* *

The returned value must not be modified.

* * @return The table of key properties. */ private Map _getKeyPropertyList() { synchronized (this) { if (_propertyList == null) { // build (lazy eval) the property list from the canonical // properties array _propertyList = new HashMap(); int len = _ca_array.length; Property prop; for (int i = len - 1; i >= 0; i--) { prop = _ca_array[i]; _propertyList.put(prop.getKeyString(_canonicalName), prop.getValueString(_canonicalName)); } } } return _propertyList; } /** *

Returns the key properties as a Hashtable. The returned * value is a Hashtable in which each key is a key in the * ObjectName's key property list and each value is the associated * value.

* *

The returned value may be unmodifiable. If it is * modifiable, changing it has no effect on this ObjectName.

* * @return The table of key properties. */ // CR 6441274 depends on the modification property defined above public Hashtable getKeyPropertyList() { return new Hashtable(_getKeyPropertyList()); } /** *

Returns a string representation of the list of key * properties specified at creation time. If this ObjectName was * constructed with the constructor {@link #ObjectName(String)}, * the key properties in the returned String will be in the same * order as in the argument to the constructor.

* * @return The key property list string. This string is * independent of whether the ObjectName is a pattern. */ public String getKeyPropertyListString() { // BEWARE : we rebuild the propertyliststring at each call !! if (_kp_array.length == 0) return ""; // the size of the string is the canonical one minus domain // part and pattern part final int total_size = _canonicalName.length() - _domain_length - 1 - (_property_list_pattern?2:0); final char[] dest_chars = new char[total_size]; final char[] value = _canonicalName.toCharArray(); writeKeyPropertyListString(value,dest_chars,0); return new String(dest_chars); } /** *

Returns the serialized string of the ObjectName. * properties specified at creation time. If this ObjectName was * constructed with the constructor {@link #ObjectName(String)}, * the key properties in the returned String will be in the same * order as in the argument to the constructor.

* * @return The key property list string. This string is * independent of whether the ObjectName is a pattern. */ private String getSerializedNameString() { // the size of the string is the canonical one final int total_size = _canonicalName.length(); final char[] dest_chars = new char[total_size]; final char[] value = _canonicalName.toCharArray(); final int offset = _domain_length+1; // copy "domain:" into dest_chars // System.arraycopy(value, 0, dest_chars, 0, offset); // Add property list string final int end = writeKeyPropertyListString(value,dest_chars,offset); // Add ",*" if necessary if (_property_list_pattern) { if (end == offset) { // Property list string is empty. dest_chars[end] = '*'; } else { // Property list string is not empty. dest_chars[end] = ','; dest_chars[end+1] = '*'; } } return new String(dest_chars); } /** *

Write a string representation of the list of key * properties specified at creation time in the given array, starting * at the specified offset. If this ObjectName was * constructed with the constructor {@link #ObjectName(String)}, * the key properties in the returned String will be in the same * order as in the argument to the constructor.

* * @return offset + #of chars written */ private int writeKeyPropertyListString(char[] canonicalChars, char[] data, int offset) { if (_kp_array.length == 0) return offset; final char[] dest_chars = data; final char[] value = _canonicalName.toCharArray(); int index = offset; final int len = _kp_array.length; final int last = len - 1; for (int i = 0; i < len; i++) { final Property prop = _kp_array[i]; final int prop_len = prop._key_length + prop._value_length + 1; System.arraycopy(value, prop._key_index, dest_chars, index, prop_len); index += prop_len; if (i < last ) dest_chars[index++] = ','; } return index; } /** * Returns a string representation of the list of key properties, * in which the key properties are sorted in lexical order. This * is used in lexicographic comparisons performed in order to * select MBeans based on their key property list. Lexical order * is the order implied by {@link String#compareTo(String) * String.compareTo(String)}. * * @return The canonical key property list string. This string is * independent of whether the ObjectName is a pattern. */ public String getCanonicalKeyPropertyListString() { if (_ca_array.length == 0) return ""; int len = _canonicalName.length(); if (_property_list_pattern) len -= 2; return _canonicalName.substring(_domain_length +1, len); } // Category : Getter methods <=================================== // Category : Utilities ----------------------------------------> /** *

Returns a string representation of the object name. The * format of this string is not specified, but users can expect * that two ObjectNames return the same string if and only if they * are equal.

* * @return a string representation of this object name. */ @Override public String toString() { return getSerializedNameString(); } String toQueryString() { return "like " + Query.value(toString()); } /** * Compares the current object name with another object name. Two * ObjectName instances are equal if and only if their canonical * forms are equal. The canonical form is the string described * for {@link #getCanonicalName()}. * * @param object The object name that the current object name is to be * compared with. * * @return True if object is an ObjectName whose * canonical form is equal to that of this ObjectName. */ @Override public boolean equals(Object object) { // same object case if (this == object) return true; // object is not an object name case if (!(object instanceof ObjectName)) return false; // equality when canonical names are the same // (because usage of intern()) ObjectName on = (ObjectName) object; String on_string = on._canonicalName; if (_canonicalName == on_string) return true; // Because we are sharing canonical form between object names, // we have finished the comparison at this stage ==> unequal return false; } /** * Returns a hash code for this object name. * */ @Override public int hashCode() { return _canonicalName.hashCode(); } /** *

Returns a quoted form of the given String, suitable for * inclusion in an ObjectName. The returned value can be used as * the value associated with a key in an ObjectName. The String * s may contain any character. Appropriate quoting * ensures that the returned value is legal in an ObjectName.

* *

The returned value consists of a quote ('"'), a sequence of * characters corresponding to the characters of s, * and another quote. Characters in s appear * unchanged within the returned value except:

* *
    *
  • A quote ('"') is replaced by a backslash (\) followed by a quote.
  • *
  • An asterisk ('*') is replaced by a backslash (\) followed by an * asterisk.
  • *
  • A question mark ('?') is replaced by a backslash (\) followed by * a question mark.
  • *
  • A backslash ('\') is replaced by two backslashes.
  • *
  • A newline character (the character '\n' in Java) is replaced * by a backslash followed by the character '\n'.
  • *
* * @param s the String to be quoted. * * @return the quoted String. * * @exception NullPointerException if s is null. * */ public static String quote(String s) throws NullPointerException { final StringBuilder buf = new StringBuilder("\""); final int len = s.length(); for (int i = 0; i < len; i++) { char c = s.charAt(i); switch (c) { case '\n': c = 'n'; buf.append('\\'); break; case '\\': case '\"': case '*': case '?': buf.append('\\'); break; } buf.append(c); } buf.append('"'); return buf.toString(); } /** *

Returns an unquoted form of the given String. If * q is a String returned by {@link #quote quote(s)}, * then unquote(q).equals(s). If there is no String * s for which quote(s).equals(q), then * unquote(q) throws an IllegalArgumentException.

* *

These rules imply that there is a one-to-one mapping between * quoted and unquoted forms.

* * @param q the String to be unquoted. * * @return the unquoted String. * * @exception IllegalArgumentException if q could not * have been returned by the {@link #quote} method, for instance * if it does not begin and end with a quote ("). * * @exception NullPointerException if q is null. * */ public static String unquote(String q) throws IllegalArgumentException, NullPointerException { final StringBuilder buf = new StringBuilder(); final int len = q.length(); if (len < 2 || q.charAt(0) != '"' || q.charAt(len - 1) != '"') throw new IllegalArgumentException("Argument not quoted"); for (int i = 1; i < len - 1; i++) { char c = q.charAt(i); if (c == '\\') { if (i == len - 2) throw new IllegalArgumentException("Trailing backslash"); c = q.charAt(++i); switch (c) { case 'n': c = '\n'; break; case '\\': case '\"': case '*': case '?': break; default: throw new IllegalArgumentException( "Bad character '" + c + "' after backslash"); } } else { switch (c) { case '*' : case '?' : case '\"': case '\n': throw new IllegalArgumentException( "Invalid unescaped character '" + c + "' in the string to unquote"); } } buf.append(c); } return buf.toString(); } /** * Defines the wildcard "*:*" ObjectName. * * @since 1.6 */ public static final ObjectName WILDCARD = Util.newObjectName("*:*"); // Category : Utilities <=================================== // Category : QueryExp Interface ----------------------------------------> /** *

Test whether this ObjectName, which may be a pattern, * matches another ObjectName. If name is a pattern, * the result is false. If this ObjectName is a pattern, the * result is true if and only if name matches the * pattern. If neither this ObjectName nor name is * a pattern, the result is true if and only if the two * ObjectNames are equal as described for the {@link * #equals(Object)} method.

* * @param name The name of the MBean to compare to. * * @return True if name matches this ObjectName. * * @exception NullPointerException if name is null. * */ public boolean apply(ObjectName name) throws NullPointerException { if (name == null) throw new NullPointerException(); if (name._domain_pattern || name._property_list_pattern || name._property_value_pattern) return false; // No pattern if (!_domain_pattern && !_property_list_pattern && !_property_value_pattern) return _canonicalName.equals(name._canonicalName); return matchDomains(name) && matchKeys(name); } private final boolean matchDomains(ObjectName name) { if (_domain_pattern) { // wildmatch domains final char[] dom_pattern = getDomain().toCharArray(); final char[] dom_string = name.getDomain().toCharArray(); return wildmatch(dom_string,dom_pattern,0,0); } return getDomain().equals(name.getDomain()); } private final boolean matchKeys(ObjectName name) { // If key property value pattern but not key property list // pattern, then the number of key properties must be equal // if (_property_value_pattern && !_property_list_pattern && (name._ca_array.length != _ca_array.length)) return false; // If key property value pattern or key property list pattern, // then every property inside pattern should exist in name // if (_property_value_pattern || _property_list_pattern) { final Map nameProps = name._getKeyPropertyList(); final Property[] props = _ca_array; final String cn = _canonicalName; for (int i = props.length - 1; i >= 0 ; i--) { // Find value in given object name for key at current // index in receiver // final Property p = props[i]; final String k = p.getKeyString(cn); final String v = nameProps.get(k); // Did we find a value for this key ? // if (v == null) return false; // If this property is ok (same key, same value), go to next // if (_property_value_pattern && (p instanceof PatternProperty)) { // wildmatch key property values final char[] val_pattern = p.getValueString(cn).toCharArray(); final char[] val_string = v.toCharArray(); if (wildmatch(val_string,val_pattern,0,0)) continue; else return false; } if (v.equals(p.getValueString(cn))) continue; return false; } return true; } // If no pattern, then canonical names must be equal // final String p1 = name.getCanonicalKeyPropertyListString(); final String p2 = getCanonicalKeyPropertyListString(); return (p1.equals(p2)); } /* Method inherited from QueryExp, no implementation needed here because ObjectName is not relative to an MBeanServer and does not contain a subquery. */ public void setMBeanServer(MBeanServer mbs) { } // Category : QueryExp Interface <========================= // Category : Comparable Interface ----------------------------------------> /** *

Compares two ObjectName instances. The ordering relation between * ObjectNames is not completely specified but is intended to be such * that a sorted list of ObjectNames will appear in an order that is * convenient for a person to read.

* *

In particular, if the two ObjectName instances have different * domains then their order is the lexicographical order of the domains. * The ordering of the key property list remains unspecified.

* *

For example, the ObjectName instances below:

*
    *
  • Shapes:type=Square,name=3
  • *
  • Colors:type=Red,name=2
  • *
  • Shapes:type=Triangle,side=isosceles,name=2
  • *
  • Colors:type=Red,name=1
  • *
  • Shapes:type=Square,name=1
  • *
  • Colors:type=Blue,name=1
  • *
  • Shapes:type=Square,name=2
  • *
  • JMImplementation:type=MBeanServerDelegate
  • *
  • Shapes:type=Triangle,side=scalene,name=1
  • *
*

could be ordered as follows:

*
    *
  • Colors:type=Blue,name=1
  • *
  • Colors:type=Red,name=1
  • *
  • Colors:type=Red,name=2
  • *
  • JMImplementation:type=MBeanServerDelegate
  • *
  • Shapes:type=Square,name=1
  • *
  • Shapes:type=Square,name=2
  • *
  • Shapes:type=Square,name=3
  • *
  • Shapes:type=Triangle,side=scalene,name=1
  • *
  • Shapes:type=Triangle,side=isosceles,name=2
  • *
* * @param name the ObjectName to be compared. * * @return a negative integer, zero, or a positive integer as this * ObjectName is less than, equal to, or greater than the * specified ObjectName. * * @since 1.6 */ public int compareTo(ObjectName name) { // (1) Compare domains // int domainValue = this.getDomain().compareTo(name.getDomain()); if (domainValue != 0) return domainValue; // (2) Compare "type=" keys // // Within a given domain, all names with missing or empty "type=" // come before all names with non-empty type. // // When both types are missing or empty, canonical-name ordering // applies which is a total order. // String thisTypeKey = this.getKeyProperty("type"); String anotherTypeKey = name.getKeyProperty("type"); if (thisTypeKey == null) thisTypeKey = ""; if (anotherTypeKey == null) anotherTypeKey = ""; int typeKeyValue = thisTypeKey.compareTo(anotherTypeKey); if (typeKeyValue != 0) return typeKeyValue; // (3) Compare canonical names // return this.getCanonicalName().compareTo(name.getCanonicalName()); } // Category : Comparable Interface <========================= // Public methods <======================================== }