0001: /*
0002: * JBoss, Home of Professional Open Source.
0003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
0004: * as indicated by the @author tags. See the copyright.txt file in the
0005: * distribution for a full listing of individual contributors.
0006: *
0007: * This is free software; you can redistribute it and/or modify it
0008: * under the terms of the GNU Lesser General Public License as
0009: * published by the Free Software Foundation; either version 2.1 of
0010: * the License, or (at your option) any later version.
0011: *
0012: * This software is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0015: * Lesser General Public License for more details.
0016: *
0017: * You should have received a copy of the GNU Lesser General Public
0018: * License along with this software; if not, write to the Free
0019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
0021: */
0022: package javax.management;
0023:
0024: import java.io.IOException;
0025: import java.io.ObjectInputStream;
0026: import java.io.ObjectOutputStream;
0027: import java.io.ObjectStreamField;
0028: import java.io.StreamCorruptedException;
0029: import java.util.ArrayList;
0030: import java.util.Collections;
0031: import java.util.Hashtable;
0032: import java.util.Iterator;
0033:
0034: import org.jboss.mx.util.ObjectNamePatternHelper;
0035: import org.jboss.mx.util.ObjectNamePatternHelper.PropertyPattern;
0036: import org.jboss.mx.util.QueryExpSupport;
0037: import org.jboss.mx.util.Serialization;
0038:
0039: /**
0040: * Object name represents the MBean reference.
0041: *
0042: * @see javax.management.MBeanServer
0043: *
0044: * @author <a href="mailto:juha@jboss.org">Juha Lindfors</a>.
0045: * @author <a href="mailto:trevor@protocool.com">Trevor Squires</a>.
0046: * @author <a href="mailto:adrian.brock@happeningtimes.com">Adrian Brock</a>.
0047: * @version $Revision: 57200 $
0048: *
0049: * <p><b>Revisions:</b>
0050: * <p><b>20020521 Adrian Brock:</b>
0051: * <ul>
0052: * <li>Allow *,* in the hashtable properties to signify a property pattern
0053: * </ul>
0054: * <p><b>20020710 Adrian Brock:</b>
0055: * <ul>
0056: * <li> Serialization
0057: * <p><b>20030114 Adrian Brock:</b>
0058: * <ul>
0059: * <li> Valid character changes for 1.2
0060: * <li> The new line character is not allowed anywhere in the
0061: * ObjectName, because of a bodge in the relation service
0062: * <li> Domains can now contain = and ,
0063: * <li> Keys are unchanged except for the new line restriction
0064: * <li> For values, "?* are allowed providing they are proceeed
0065: * by a \ and the escaping is in enclosed in a "" pair.
0066: * A new line must be \n inside a "" pair.
0067: * =,: are allowed inside a "" pair.
0068: * </ul>
0069: */
0070: public class ObjectName implements java.io.Serializable, QueryExp {
0071:
0072: // Attributes ----------------------------------------------------
0073: private transient boolean hasPattern = false;
0074: private transient boolean hasDomainPattern = false;
0075: private transient boolean hasPropertyPattern = false;
0076: private transient Hashtable propertiesHash = null;
0077: private transient ArrayList propertiesList = null;
0078:
0079: private transient String domain = null;
0080: // The key property list string. This string is independent of whether the ObjectName is a pattern.
0081: private transient String kProps = null;
0082: // The canonical key property list string. This string is independent of whether the ObjectName is a pattern.
0083: private transient String ckProps = null;
0084: // The canonical name.
0085: private transient String cName = null;
0086: // The properties as passed to the constructor
0087: private transient String constructorProps;
0088:
0089: private transient int hash;
0090: private transient PropertyPattern propertyPattern;
0091:
0092: // Static --------------------------------------------------------
0093:
0094: private static final long serialVersionUID;
0095: private static final ObjectStreamField[] serialPersistentFields;
0096:
0097: static {
0098: switch (Serialization.version) {
0099: case Serialization.V1R0:
0100: serialVersionUID = -5467795090068647408L;
0101: serialPersistentFields = new ObjectStreamField[] {
0102: new ObjectStreamField("domain", String.class),
0103: new ObjectStreamField("propertyList",
0104: Hashtable.class),
0105: new ObjectStreamField("propertyListString",
0106: String.class),
0107: new ObjectStreamField("canonicalName", String.class),
0108: new ObjectStreamField("pattern", Boolean.TYPE),
0109: new ObjectStreamField("propertyPattern",
0110: Boolean.TYPE) };
0111: break;
0112: default:
0113: serialVersionUID = 1081892073854801359L;
0114: serialPersistentFields = new ObjectStreamField[0];
0115: }
0116: }
0117:
0118: /**
0119: * Return an instance of an ObjectName
0120: *
0121: * @todo Review: return common object names?
0122: * @param name a string representation of the object name
0123: * @exception MalformedObjectNameException for an invalid object name
0124: * @exception NullPointerException for a null name
0125: */
0126: public static ObjectName getInstance(String name)
0127: throws MalformedObjectNameException, NullPointerException {
0128: return new ObjectName(name);
0129: }
0130:
0131: /**
0132: * Return an instance of an ObjectName
0133: *
0134: * @todo Review: return common object names?
0135: * @param domain the domain of the object name
0136: * @param key the key of the single property
0137: * @param value the value of the single property
0138: * @exception MalformedObjectNameException for an invalid object name
0139: * @exception NullPointerException for a null parameter
0140: */
0141: public static ObjectName getInstance(String domain, String key,
0142: String value) throws MalformedObjectNameException,
0143: NullPointerException {
0144: return new ObjectName(domain, key, value);
0145: }
0146:
0147: /**
0148: * Return an instance of an ObjectName
0149: *
0150: * @todo Review: return common object names?
0151: * @param domain the domain of the object name
0152: * @param table of hashtable for key property pairs
0153: * @exception MalformedObjectNameException for an invalid object name
0154: * @exception NullPointerException for a null parameter
0155: */
0156: public static ObjectName getInstance(String domain, Hashtable table)
0157: throws MalformedObjectNameException, NullPointerException {
0158: return new ObjectName(domain, table);
0159: }
0160:
0161: /** Return an instance of ObjectName that can be used anywhere the given
0162: * object can be used. The returned object may be of a subclass of
0163: * ObjectName. If name is of a subclass of ObjectName, it is not guaranteed
0164: * that the returned object will be of the same class.
0165: *
0166: * The returned value may or may not be identical to name. Calling this
0167: * method twice with the same parameters may return the same object or two
0168: * equal but not identical objects.
0169: *
0170: * Since ObjectName is immutable, it is not usually useful to make a copy
0171: * of an ObjectName. The principal use of this method is to guard against a
0172: * malicious caller who might pass an instance of a subclass with surprising
0173: * behaviour to sensitive code. Such code can call this method to obtain an
0174: * ObjectName that is known not to have surprising behaviour.
0175: *
0176: * @param name - an instance of the ObjectName class or of a subclass
0177: * @return an instance of ObjectName or a subclass that is known to have the
0178: * same semantics. If name respects the semantics of ObjectName, then the
0179: * returned object is equal (though not necessarily identical) to name.
0180: * @throws NullPointerException
0181: */
0182: public static ObjectName getInstance(ObjectName name)
0183: throws NullPointerException {
0184: return name;
0185: }
0186:
0187: /**
0188: * Quotes the passed string suitable for use as a value in an
0189: * ObjectName.
0190: *
0191: * @param value the string to quote
0192: * @return the quoted string
0193: * @exception NullPointerException for a null string
0194: */
0195: public static String quote(String value)
0196: throws NullPointerException {
0197: if (value == null)
0198: throw new NullPointerException("null value");
0199:
0200: StringBuffer buffer = new StringBuffer(value.length() + 10);
0201: buffer.append('"');
0202: char[] chars = value.toCharArray();
0203: for (int i = 0; i < chars.length; i++) {
0204: switch (chars[i]) {
0205: case '"':
0206: case '*':
0207: case '?':
0208: case '\\':
0209: buffer.append('\\').append(chars[i]);
0210: break;
0211: case '\n':
0212: buffer.append('\\').append('n');
0213: break;
0214: default:
0215: buffer.append(chars[i]);
0216: }
0217: }
0218: buffer.append('"');
0219:
0220: return buffer.toString();
0221: }
0222:
0223: /**
0224: * Unquotes a string, unquote(quote(s)).equals(s) is always true.
0225: *
0226: * @param value the string to unquote
0227: * @return the unquoted string
0228: * @exception IllegalArgumentException when the string is
0229: * not of a form that can be unquoted.
0230: * @exception NullPointerException for a null string
0231: */
0232: public static String unquote(String value)
0233: throws IllegalArgumentException, NullPointerException {
0234: if (value == null)
0235: throw new NullPointerException("null value");
0236: if (value.length() == 0)
0237: throw new IllegalArgumentException("Empty value");
0238:
0239: StringBuffer buffer = new StringBuffer(value.length());
0240:
0241: char[] chars = value.toCharArray();
0242:
0243: boolean inQuote = false;
0244: boolean escape = false;
0245:
0246: for (int i = 0; i < chars.length; ++i) {
0247: // Valid escape character?
0248: char c = chars[i];
0249: if (escape) {
0250: switch (c) {
0251: case '\\':
0252: case '"':
0253: case '*':
0254: case '?':
0255: escape = false;
0256: buffer.append(c);
0257: break;
0258: case 'n':
0259: escape = false;
0260: buffer.append('\n');
0261: break;
0262:
0263: // Invalid escape character
0264: default:
0265: throw new IllegalArgumentException(
0266: "The value "
0267: + value
0268: + " contains an invalid escape sequence backslash "
0269: + c);
0270: }
0271: }
0272:
0273: // Starting a quote
0274: else if (inQuote == false && c == '"')
0275: inQuote = true;
0276:
0277: // Ending a quote
0278: else if (inQuote && c == '"')
0279: inQuote = false;
0280:
0281: // Escaping
0282: else if (inQuote && c == '\\')
0283: escape = true;
0284:
0285: // Inside a quote
0286: else if (inQuote) {
0287: switch (c) {
0288: case '"':
0289: throw new IllegalArgumentException(
0290: "The value "
0291: + value
0292: + " cannot contain quote inside a quote pair, use backslash quote");
0293: case '*':
0294: case '?':
0295: throw new IllegalArgumentException("The value "
0296: + value + " cannot contain " + c
0297: + " inside a quote pair, use backslash "
0298: + c);
0299: case '\n':
0300: throw new IllegalArgumentException(
0301: "The value "
0302: + value
0303: + " cannot contain a new line inside a quote pair, use backslash n");
0304: }
0305: buffer.append(c);
0306: }
0307:
0308: // Plain text
0309: else
0310: throw new IllegalArgumentException("The value " + value
0311: + " is not enclosed in quotes");
0312: }
0313:
0314: if (inQuote)
0315: throw new IllegalArgumentException(
0316: "Unterminated quote pair, missing quote");
0317: if (escape)
0318: throw new IllegalArgumentException(
0319: "Unterminated escape, missing one of backslash quote asterisk question mark or n");
0320:
0321: return buffer.toString();
0322: }
0323:
0324: // Constructors --------------------------------------------------
0325:
0326: /**
0327: *
0328: * @param name a string representation of the object name
0329: * @exception MalformedObjectNameException for an invalid object name
0330: * @exception NullPointerException for a null name
0331: */
0332: public ObjectName(String name) throws MalformedObjectNameException,
0333: NullPointerException {
0334: init(name);
0335: }
0336:
0337: /**
0338: * Construct a new ObjectName
0339: *
0340: * @param domain the domain of the object name
0341: * @param key the key of the single property
0342: * @param value the value of the single property
0343: * @exception MalformedObjectNameException for an invalid object name
0344: * @exception NullPointerException for a null parameter
0345: */
0346: public ObjectName(String domain, String key, String value)
0347: throws MalformedObjectNameException, NullPointerException {
0348: /**
0349: * According to the JMX 1.2 spec javadoc, should throw NullPointerException
0350: * if any of the parameters are null. -TME
0351: */
0352: if (null == key)
0353: throw new NullPointerException(
0354: "properties key cannot be null");
0355: if (null == value)
0356: throw new NullPointerException(
0357: "properties value cannot be null");
0358:
0359: if (key.length() == 0)
0360: throw new MalformedObjectNameException(
0361: "properties key cannot be empty");
0362: if (value.length() == 0)
0363: throw new MalformedObjectNameException(
0364: "properties value cannot be empty");
0365:
0366: initDomain(domain, null);
0367:
0368: Hashtable ptable = new Hashtable();
0369: ptable.put(key, value);
0370:
0371: initProperties(ptable, null);
0372: }
0373:
0374: /**
0375: * Construct a new ObjectName
0376: *
0377: * @param domain the domain of the object name
0378: * @param table of hashtable for key property pairs
0379: * @exception MalformedObjectNameException for an invalid object name
0380: * @exception NullPointerException for a null parameter
0381: */
0382: public ObjectName(String domain, Hashtable table)
0383: throws MalformedObjectNameException, NullPointerException {
0384: if (table == null)
0385: throw new NullPointerException("null table");
0386:
0387: initDomain(domain, null);
0388:
0389: if (table.size() < 1)
0390: throw new MalformedObjectNameException("empty table");
0391:
0392: initProperties((Hashtable) table.clone(), null);
0393: }
0394:
0395: // Public ------------------------------------------------------
0396: public boolean equals(Object object) {
0397: if (object == this )
0398: return true;
0399: if (object == null)
0400: return false;
0401:
0402: if (object instanceof ObjectName) {
0403: ObjectName oname = (ObjectName) object;
0404: return (oname.hash == hash && domain.equals(oname.domain) && ckProps
0405: .equals(oname.ckProps));
0406: }
0407:
0408: return false;
0409: }
0410:
0411: public int hashCode() {
0412: return hash;
0413: }
0414:
0415: public String toString() {
0416: return getCanonicalName();
0417: }
0418:
0419: public boolean isPattern() {
0420: return hasPattern;
0421: }
0422:
0423: /**
0424: * The canonical form of the name is a String consisting of the domain part, a colon (:),
0425: * the canonical key property list, and a pattern indication.
0426: *
0427: * The canonical key property list is the same string as described for getCanonicalKeyPropertyListString().
0428: * The pattern indication is:
0429: * - empty for an ObjectName that is not a property pattern;
0430: * - an asterisk for an ObjectName that is a property pattern with no keys; or
0431: * - a comma and an asterisk (,*) for an ObjectName that is a property pattern with at least one key.
0432: */
0433: public String getCanonicalName() {
0434: if (cName == null) {
0435: cName = domain + ":" + ckProps;
0436: if (ckProps.length() == 0 && hasPropertyPattern) {
0437: cName += "*";
0438: }
0439: if (ckProps.length() > 0 && hasPropertyPattern) {
0440: cName += ",*";
0441: }
0442: }
0443: return cName;
0444: }
0445:
0446: public String getDomain() {
0447: return domain;
0448: }
0449:
0450: public String getKeyProperty(String property)
0451: throws NullPointerException {
0452: return (String) propertiesHash.get(property);
0453: }
0454:
0455: public Hashtable getKeyPropertyList() {
0456: return (Hashtable) propertiesHash.clone();
0457: }
0458:
0459: /**
0460: * Construct the key property list, ignoring patters
0461: * Note, it might not be the same order
0462: */
0463: public String getKeyPropertyListString() {
0464: if (constructorProps != null)
0465: return constructorProps;
0466: else if (kProps == null) {
0467: StringBuffer buffer = new StringBuffer();
0468: Iterator it = propertiesHash.keySet().iterator();
0469:
0470: // if the list of property keys is available, we use it
0471: if (propertiesList != null)
0472: it = propertiesList.iterator();
0473:
0474: for (int i = 0; it.hasNext(); i++) {
0475: if (i > 0)
0476: buffer.append(",");
0477: String key = (String) it.next();
0478: String val = (String) propertiesHash.get(key);
0479: buffer.append(key + "=" + val);
0480: }
0481: kProps = buffer.toString();
0482: }
0483: return kProps;
0484: }
0485:
0486: public String getCanonicalKeyPropertyListString() {
0487: if (ckProps == null) {
0488: StringBuffer buffer = new StringBuffer();
0489: ArrayList list = new ArrayList(propertiesHash.keySet());
0490: Collections.sort(list);
0491: Iterator it = list.iterator();
0492: for (int i = 0; it.hasNext(); i++) {
0493: if (i > 0)
0494: buffer.append(",");
0495: String key = (String) it.next();
0496: String val = (String) propertiesHash.get(key);
0497: buffer.append(key + "=" + val);
0498: }
0499: ckProps = buffer.toString();
0500: }
0501: return ckProps;
0502: }
0503:
0504: public boolean isPropertyPattern() {
0505: return hasPropertyPattern;
0506: }
0507:
0508: public boolean isDomainPattern() {
0509: return hasDomainPattern;
0510: }
0511:
0512: // QueryExp Implementation -------------------------------------
0513:
0514: public boolean apply(ObjectName name) throws NullPointerException {
0515: if (name.isPattern())
0516: return false;
0517: if (this == name)
0518: return true;
0519:
0520: if (ObjectNamePatternHelper.patternMatch(name.getDomain(), this
0521: .getDomain())) {
0522: if (propertyPattern == null)
0523: propertyPattern = new PropertyPattern(this );
0524: return propertyPattern.patternMatch(name);
0525: }
0526: return false;
0527: }
0528:
0529: public void setMBeanServer(MBeanServer server) {
0530: QueryExpSupport.server.set(server);
0531: }
0532:
0533: // Private -----------------------------------------------------
0534:
0535: /**
0536: * constructs an object name from a string
0537: */
0538: private void init(String name) throws MalformedObjectNameException {
0539: if (name == null)
0540: throw new NullPointerException("null name");
0541:
0542: if (name.length() == 0)
0543: name = "*:*";
0544:
0545: int domainSep = name.indexOf(':');
0546:
0547: if (-1 == domainSep)
0548: throw new MalformedObjectNameException("missing domain");
0549:
0550: initDomain(name.substring(0, domainSep), name);
0551: constructorProps = name.substring(domainSep + 1);
0552: initProperties(constructorProps, name);
0553: // Remove any pattern
0554: if (constructorProps.equals("*"))
0555: constructorProps = "";
0556: else if (constructorProps.startsWith("*,"))
0557: constructorProps = constructorProps.substring(2);
0558: else if (constructorProps.endsWith(",*"))
0559: constructorProps = constructorProps.substring(0,
0560: constructorProps.length() - 2);
0561: }
0562:
0563: /**
0564: * checks for domain patterns and illegal characters
0565: */
0566: private void initDomain(String dstring, String name)
0567: throws MalformedObjectNameException {
0568: if (null == dstring)
0569: throw new NullPointerException("null domain");
0570:
0571: checkIllegalDomain(dstring, name);
0572:
0573: if (dstring.indexOf('*') > -1 || dstring.indexOf('?') > -1) {
0574: this .hasPattern = true;
0575: this .hasDomainPattern = true;
0576: }
0577:
0578: this .domain = dstring;
0579: }
0580:
0581: /**
0582: * takes the properties string and breaks it up into key/value pairs for
0583: * insertion into a newly created hashtable.
0584: *
0585: * minimal validation is performed so that it doesn't blow up when
0586: * constructing the kvp strings.
0587: *
0588: * checks for duplicate keys
0589: *
0590: * detects property patterns
0591: *
0592: */
0593: private void initProperties(String properties, String name)
0594: throws MalformedObjectNameException {
0595: if (null == properties || properties.length() < 1)
0596: throw new MalformedObjectNameException(
0597: addDebugObjectName(name)
0598: + "null or empty properties");
0599:
0600: Hashtable ptable = new Hashtable();
0601: this .propertiesList = new ArrayList();
0602:
0603: char[] chars = properties.toCharArray();
0604:
0605: String key = null;
0606: int start = 0;
0607: boolean inKey = true;
0608: boolean inQuote = false;
0609: boolean escape = false;
0610: boolean endOrTerminate = false;
0611:
0612: for (int current = 0; current < chars.length; ++current) {
0613: char c = chars[current];
0614:
0615: // Previous character was the backslash escape
0616: if (escape)
0617: escape = false;
0618:
0619: // In a quote
0620: else if (inQuote) {
0621: // End the quote
0622: if (c == '"') {
0623: inQuote = false;
0624: endOrTerminate = true;
0625: }
0626:
0627: // Start escaping
0628: else if (c == '\\')
0629: escape = true;
0630: }
0631:
0632: // Processing the key
0633: else if (inKey) {
0634: // Found the terminator
0635: if (c == '=' || c == ',') {
0636: if (current == start)
0637: throw new MalformedObjectNameException(
0638: addDebugObjectName(name) + "Empty key");
0639: key = properties.substring(start, current);
0640:
0641: // Didn't get an equals so the only thing valid is asterisk
0642: if (c == ',') {
0643: if (key.equals("*")) {
0644: if (hasPropertyPattern)
0645: throw new MalformedObjectNameException(
0646: addDebugObjectName(name)
0647: + "A property pattern may only contain one * property");
0648: this .hasPropertyPattern = true;
0649: this .hasPattern = true;
0650: } else
0651: throw new MalformedObjectNameException(
0652: addDebugObjectName(name)
0653: + "Invalid key/value data "
0654: + key);
0655: } else {
0656: // Check for a duplicate key and process the value
0657: if (ptable.containsKey(key))
0658: throw new MalformedObjectNameException(
0659: addDebugObjectName(name)
0660: + "Duplicate key " + key);
0661: inKey = false;
0662: }
0663: start = current + 1;
0664: }
0665: }
0666:
0667: // Processing a value
0668: else {
0669: // Starting a quote
0670: if (c == '"')
0671: inQuote = true;
0672:
0673: // Found the terminator
0674: else if (c == ',') {
0675: // Add the key, value pair and process the next pair
0676: if (current == start)
0677: throw new MalformedObjectNameException(
0678: addDebugObjectName(name)
0679: + "Invalid key/value data "
0680: + key);
0681: else {
0682: propertiesList.add(key);
0683: ptable.put(key, properties.substring(start,
0684: current));
0685: }
0686:
0687: inKey = true;
0688: endOrTerminate = false;
0689: start = current + 1;
0690: }
0691:
0692: // expect a terminator after the closing quote
0693: else if (endOrTerminate) {
0694: throw new MalformedObjectNameException(
0695: addDebugObjectName(name)
0696: + "Invalid key/value data " + key);
0697: }
0698: }
0699: }
0700:
0701: // Fell off the end while processing a key
0702: if (inKey) {
0703: if (start == chars.length)
0704: throw new MalformedObjectNameException(
0705: addDebugObjectName(name)
0706: + "An ObjectName cannot end with a comma");
0707:
0708: // Might be ok if the remainder is an asterisk
0709: key = properties.substring(start, chars.length);
0710: if (key.equals("*")) {
0711: if (hasPropertyPattern)
0712: throw new MalformedObjectNameException(
0713: addDebugObjectName(name)
0714: + "A property pattern may only contain one * property");
0715:
0716: this .hasPropertyPattern = true;
0717: this .hasPattern = true;
0718: } else
0719: throw new MalformedObjectNameException(
0720: addDebugObjectName(name)
0721: + "Invalid key/value data " + key);
0722: }
0723:
0724: // Fell off the end while processing a value
0725: if (inKey == false) {
0726: // No value
0727: if (start == chars.length)
0728: throw new MalformedObjectNameException(
0729: addDebugObjectName(name)
0730: + "Invalid key/value data " + key);
0731: else {
0732: propertiesList.add(key);
0733: ptable.put(key, properties.substring(start,
0734: chars.length));
0735: }
0736: }
0737:
0738: initProperties(ptable, name);
0739: }
0740:
0741: /**
0742: * validates incoming properties hashtable
0743: *
0744: * builds canonical string
0745: *
0746: * precomputes the hashcode
0747: */
0748: private void initProperties(Hashtable properties, String name)
0749: throws MalformedObjectNameException {
0750: if (null == properties
0751: || (!this .hasPropertyPattern && properties.size() < 1))
0752: throw new MalformedObjectNameException(
0753: addDebugObjectName(name)
0754: + "null or empty properties");
0755:
0756: Iterator it = properties.keySet().iterator();
0757: ArrayList list = new ArrayList();
0758:
0759: while (it.hasNext()) {
0760: String key = null;
0761: try {
0762: key = (String) it.next();
0763: } catch (ClassCastException e) {
0764: throw new MalformedObjectNameException(
0765: addDebugObjectName(name)
0766: + "key is not a string " + key);
0767: }
0768:
0769: String val = null;
0770: try {
0771: val = (String) properties.get(key);
0772: } catch (ClassCastException e) {
0773: throw new MalformedObjectNameException(
0774: addDebugObjectName(name)
0775: + "value is not a string " + val);
0776: }
0777:
0778: if (key.equals("*") && val.equals("*")) {
0779: it.remove();
0780: this .hasPropertyPattern = true;
0781: this .hasPattern = true;
0782: continue;
0783: }
0784:
0785: if (key.length() == 0)
0786: throw new MalformedObjectNameException(
0787: addDebugObjectName(name)
0788: + "key has no length =" + val);
0789:
0790: if (val.length() == 0)
0791: throw new MalformedObjectNameException(
0792: addDebugObjectName(name)
0793: + "value has no length =" + val);
0794:
0795: checkIllegalKey(key, name);
0796: checkIllegalValue(val, name);
0797:
0798: list.add(new String(key + "=" + val));
0799: }
0800:
0801: Collections.sort(list);
0802: StringBuffer strBuffer = new StringBuffer();
0803:
0804: it = list.iterator();
0805: while (it.hasNext()) {
0806: strBuffer.append(it.next());
0807: if (it.hasNext()) {
0808: strBuffer.append(',');
0809: }
0810: }
0811:
0812: if (this .hasPropertyPattern) {
0813: if (properties.size() > 0) {
0814: strBuffer.append(",*");
0815: } else {
0816: strBuffer.append("*");
0817: }
0818: }
0819:
0820: this .propertiesHash = properties;
0821: this .kProps = getKeyPropertyListString();
0822: this .ckProps = getCanonicalKeyPropertyListString();
0823: this .hash = getCanonicalName().hashCode();
0824: }
0825:
0826: private void checkIllegalKey(String key, String name)
0827: throws MalformedObjectNameException {
0828: char[] chars = key.toCharArray();
0829:
0830: for (int i = 0; i < chars.length; ++i) {
0831: switch (chars[i]) {
0832: case ':':
0833: case ',':
0834: case '=':
0835: case '*':
0836: case '?':
0837: throw new MalformedObjectNameException(
0838: addDebugObjectName(name) + "The key " + key
0839: + " cannot contain a " + chars[i]
0840: + " character");
0841: case '\n':
0842: throw new MalformedObjectNameException(
0843: addDebugObjectName(name)
0844: + "The key "
0845: + key
0846: + " cannot contain a new line character");
0847: }
0848: }
0849: }
0850:
0851: private void checkIllegalValue(String value, String name)
0852: throws MalformedObjectNameException {
0853: char[] chars = value.toCharArray();
0854:
0855: boolean inQuote = false;
0856: boolean escape = false;
0857:
0858: for (int i = 0; i < chars.length; ++i) {
0859: // Valid escape character?
0860: char c = chars[i];
0861: if (escape) {
0862: switch (c) {
0863: case '\\':
0864: case 'n':
0865: case '"':
0866: case '*':
0867: case '?':
0868: escape = false;
0869: break;
0870:
0871: // Invalid escape character
0872: default:
0873: throw new MalformedObjectNameException(
0874: addDebugObjectName(name)
0875: + "The value "
0876: + value
0877: + " contains an invalid escape sequence backslash "
0878: + c);
0879: }
0880: }
0881:
0882: // Starting a quote
0883: else if (inQuote == false && c == '"')
0884: inQuote = true;
0885:
0886: // Ending a quote
0887: else if (inQuote && c == '"')
0888: inQuote = false;
0889:
0890: // Escaping
0891: else if (inQuote && c == '\\')
0892: escape = true;
0893:
0894: // Inside a quote
0895: else if (inQuote) {
0896: switch (c) {
0897: case '"':
0898: throw new MalformedObjectNameException(
0899: addDebugObjectName(name)
0900: + "The value "
0901: + value
0902: + " cannot contain quote inside a quote pair, use backslash quote");
0903: case '*':
0904: case '?':
0905: throw new MalformedObjectNameException(
0906: addDebugObjectName(name)
0907: + "The value "
0908: + value
0909: + " cannot contain "
0910: + c
0911: + " inside a quote pair, use backslash "
0912: + c);
0913: case '\n':
0914: throw new MalformedObjectNameException(
0915: addDebugObjectName(name)
0916: + "The value "
0917: + value
0918: + " cannot contain a new line inside a quote pair, use backslash n");
0919: }
0920: }
0921:
0922: // Plain text
0923: else {
0924: switch (c) {
0925: case ':':
0926: case ',':
0927: case '=':
0928: case '*':
0929: case '?':
0930: throw new MalformedObjectNameException(
0931: addDebugObjectName(name)
0932: + "The value "
0933: + value
0934: + " cannot contain "
0935: + c
0936: + " use quote backslash "
0937: + c
0938: + " quote or ObjectName.quote(String)");
0939: case '\n':
0940: throw new MalformedObjectNameException(
0941: addDebugObjectName(name)
0942: + "The value "
0943: + value
0944: + " cannot contain a new line use quote backslash n quote or ObjectName.quote(String)");
0945: }
0946: }
0947: }
0948:
0949: if (inQuote)
0950: throw new MalformedObjectNameException(
0951: addDebugObjectName(name)
0952: + "Unterminated quote pair, missing quote");
0953: if (escape)
0954: throw new MalformedObjectNameException(
0955: addDebugObjectName(name)
0956: + "Unterminated escape, missing one of backslash quote asterisk question mark or n");
0957: }
0958:
0959: /**
0960: * returns true if the domain contains illegal characters
0961: */
0962: private void checkIllegalDomain(String dom, String name)
0963: throws MalformedObjectNameException {
0964: char[] chars = dom.toCharArray();
0965:
0966: for (int i = 0; i < chars.length; i++) {
0967: switch (chars[i]) {
0968: case ':':
0969: throw new MalformedObjectNameException(
0970: addDebugObjectName(name) + "The domain " + dom
0971: + " cannot contain a : character");
0972: case '\n':
0973: throw new MalformedObjectNameException(
0974: addDebugObjectName(name)
0975: + "The domain "
0976: + dom
0977: + " cannot contain a new line character");
0978: }
0979: }
0980: }
0981:
0982: private String addDebugObjectName(String name) {
0983: if (name == null)
0984: return "";
0985: else
0986: return name + " is not a valid ObjectName. ";
0987: }
0988:
0989: private void readObject(ObjectInputStream ois) throws IOException,
0990: ClassNotFoundException {
0991: switch (Serialization.version) {
0992: case Serialization.V1R0:
0993: ObjectInputStream.GetField getField = ois.readFields();
0994: try {
0995: String name = (String) getField.get("canonicalName",
0996: null);
0997: if (name == null)
0998: throw new StreamCorruptedException(
0999: "No canonical name for jmx1.0?");
1000: init(name);
1001: } catch (MalformedObjectNameException e) {
1002: throw new StreamCorruptedException(e.toString());
1003: }
1004: break;
1005: default:
1006: try {
1007: init((String) ois.readObject());
1008: } catch (MalformedObjectNameException e) {
1009: throw new StreamCorruptedException(e.toString());
1010: }
1011: }
1012: }
1013:
1014: private void writeObject(ObjectOutputStream oos) throws IOException {
1015: switch (Serialization.version) {
1016: case Serialization.V1R0:
1017: ObjectOutputStream.PutField putField = oos.putFields();
1018: putField.put("domain", domain);
1019: putField.put("propertyList", propertiesHash);
1020: putField.put("propertyListString", ckProps);
1021: putField.put("canonicalName", domain + ":" + ckProps);
1022: putField.put("pattern", hasPattern);
1023: putField.put("propertyPattern", hasPropertyPattern);
1024: oos.writeFields();
1025: break;
1026: default:
1027: String props = getKeyPropertyListString();
1028: StringBuffer buffer = new StringBuffer(domain.length()
1029: + props.length() + 3);
1030: buffer.append(domain);
1031: buffer.append(':');
1032: buffer.append(props);
1033: if (hasPropertyPattern) {
1034: if (propertiesHash.size() > 0)
1035: buffer.append(',');
1036: buffer.append('*');
1037: }
1038: oos.writeObject(buffer.toString());
1039: }
1040: }
1041: }
|