001: /* JFox, the OpenSource J2EE Application Server
002: *
003: * Copyright (C) 2002 huihoo.org
004: * Distributable under GNU LGPL license
005: * See the GNU Lesser General Public License for more details.
006: */
007:
008: package javax.management;
009:
010: import java.io.Serializable;
011: import java.io.ObjectInputStream;
012: import java.io.IOException;
013: import java.io.InvalidObjectException;
014: import java.io.ObjectOutputStream;
015: import java.util.Hashtable;
016: import java.util.Arrays;
017: import java.util.Map;
018: import java.util.HashMap;
019: import java.util.Iterator;
020: import java.util.ArrayList;
021: import java.util.List;
022:
023: /**
024: * <p>Represents the object name of an MBean, or a pattern that can
025: * match the names of several MBeans. Instances of this class are
026: * immutable.</p>
027: *
028: * <p>An instance of this class can
029: * be used to represent:
030: * <ul>
031: * <li> An object name
032: * <li> An object name pattern, within the context of a query
033: * </ul></p>
034: *
035: * <p>An object name consists of two parts, the domain and the key
036: * properties.</p>
037: *
038: * <p>The <em>domain</em> is a string of characters not including
039: * the character colon (<code>:</code>).</p>
040: *
041: * <p>If the domain includes at least one occurrence of the wildcard
042: * characters asterisk (<code>*</code>) or question mark
043: * (<code>?</code>), then the object name is a pattern. The asterisk
044: * matches any sequence of zero or more characters, while the question
045: * mark matches any single character.</p>
046: *
047: * <p>If the domain is empty, it will be replaced in certain contexts
048: * by the <em>default domain</em> of the MBean server in which the
049: * ObjectName is used.</p>
050: *
051: * <p>The <em>key properties</em> are an unordered set of keys and
052: * associated values.</p>
053: *
054: * <p>Each <em>key</em> is a nonempty string of characters which may
055: * not contain any of the characters comma (<code>,</code>), equals
056: * (<code>=</code>), colon, asterisk, or question mark. The same key
057: * may not occur twice in a given ObjectName.</p>
058: *
059: * <p>Each <em>value</em> associated with a key is a string of
060: * characters that is either unquoted or quoted.</p>
061: *
062: * <p>An <em>unquoted value</em> is a possibly empty string of
063: * characters which may not contain any of the characters comma,
064: * equals, colon, quote, asterisk, or question mark.</p>
065: *
066: * <p>A <em>quoted value</em> consists of a quote (<code>"</code>),
067: * followed by a possibly empty string of characters, followed by
068: * another quote. Within the string of characters, the backslash
069: * (<code>\</code>) has a special meaning. It must be followed by
070: * one of the following characters:</p>
071: *
072: * <ul>
073: * <li>Another backslash. The second backslash has no special
074: * meaning and the two characters represent a single blackslash.
075: *
076: * <li>The character 'n'. The two characters represent a newline
077: * ('\n' in Java).
078: *
079: * <li>A quote. The two characters represent a quote, and that quote
080: * is not considered to terminate the quoted value. An ending closing
081: * quote must be present for the quoted value to be valid.
082: *
083: * <li>A question mark (?) or star (*). The two characters represent
084: * a question mark or star respectively.
085: * </ul>
086: *
087: * <p>A quote, question mark, or star may not appear inside a quoted
088: * value except immediately after an odd number of consecutive
089: * backslashes.</p>
090: *
091: * <p>The quotes surrounding a quoted value, and any backslashes
092: * within that value, are considered to be part of the value.</p>
093: *
094: * <p>An ObjectName may be a <em>property pattern</em>. In this case
095: * it may have zero or more keys and associated values. It matches a
096: * nonpattern ObjectName whose domain matches and that contains the
097: * same keys and associated values, as well as possibly other keys and
098: * values.</p>
099: *
100: * <p>An ObjectName is a pattern if its domain contains a wildcard or
101: * if the ObjectName is a property pattern.</p>
102: *
103: * <p>If an ObjectName is not a pattern, it must contain at least one
104: * key with its associated value.</p>
105: *
106: * <p>An ObjectName can be written as a String with the following
107: * elements in order:</p>
108: *
109: * <ul>
110: * <li>The domain.
111: * <li>A colon (<code>:</code>).
112: * <li>A key property list as defined below.
113: * </ul>
114: *
115: * <p>A key property list written as a String is a comma-separated
116: * list of elements. Each element is either an asterisk or a key
117: * property. A key property consists of a key, an equals
118: * (<code>=</code>), and the associated value.</p>
119: *
120: * <p>At most one element of a key property list may be an asterisk.
121: * If the key property list contains an asterisk element, the
122: * ObjectName is a property pattern.</p>
123: *
124: * <p>Spaces have no special significance in a String representing an
125: * ObjectName. For example, the String:
126: * <pre>
127: * domain: key1 = value1 , key2 = value2
128: * </pre>
129: * represents an ObjectName with two keys. The name of each key
130: * contains six characters, of which the first and last are spaces.
131: * The value associated with the key <code>" key1 "</code>
132: * also begins and ends with a space.</p>
133: *
134: * <p>In addition to the restrictions on characters spelt out above,
135: * no part of an ObjectName may contain a newline character
136: * (<code>'\n'</code>), whether the domain, a key, or a value, whether
137: * quoted or unquoted. The newline character can be represented in a
138: * quoted value with the sequence <code>\n</code>.
139: *
140: * <p>The rules on special characters and quoting apply regardless of
141: * which constructor is used to make an ObjectName.</p>
142: *
143: * <p>To avoid collisions between MBeans supplied by different
144: * vendors, a useful convention is to begin the domain name with the
145: * reverse DNS name of the organization that specifies the MBeans,
146: * followed by a period and a string whose interpretation is
147: * determined by that organization. For example, MBeans specified by
148: * Sun Microsystems Inc., DNS name <code>sun.com</code>, would have
149: * domains such as <code>com.sun.MyDomain</code>. This is essentially
150: * the same convention as for Java-language package names.</p>
151: *
152: * @author <a href="mailto:young_yy@hotmail.org">Young Yang</a>
153: */
154:
155: public class ObjectName implements QueryExp, Serializable {
156:
157: private String domain = "";
158: private HashMap propertyMap = new HashMap();
159: private String propertyListString = "";
160: private String canonicalName = ""; // standard name, propertyMap have sorted, not the ORIGINAL name
161: private boolean pattern = false;
162: private boolean propertyPattern = false;
163:
164: public ObjectName(String name) throws MalformedObjectNameException {
165: construct(name);
166: }
167:
168: public ObjectName(String domain, String key, String value)
169: throws MalformedObjectNameException {
170: if (domain == null) {
171: throw new MalformedObjectNameException(
172: "ObjectName: domain can not be null");
173: }
174: if (key == null || value == null)
175: throw new MalformedObjectNameException(
176: "ObjectName: Neither the key nor the value can be null");
177:
178: Map table = new HashMap();
179: table.put(key, value);
180: construct(domain, table);
181: }
182:
183: public ObjectName(String domain, Hashtable table)
184: throws MalformedObjectNameException {
185: if (domain == null) {
186: throw new MalformedObjectNameException(
187: "ObjectName: domain can not be null");
188: }
189: if (table == null || table.isEmpty())
190: throw new MalformedObjectNameException(
191: "ObjectName: Hashtable is null or empty.");
192: construct(domain, new HashMap(table));
193: }
194:
195: public boolean equals(Object obj) {
196: if (!(obj instanceof ObjectName)) {
197: return false;
198: } else {
199: ObjectName objectname = (ObjectName) obj;
200: return canonicalName.equals(objectname.getCanonicalName());
201: }
202: }
203:
204: public int hashCode() {
205: return canonicalName.hashCode();
206: }
207:
208: public String toString() {
209: String str = domain
210: + ":"
211: + (propertyPattern ? propertyListString.length() != 0 ? "*,"
212: : "*"
213: : "") + propertyListString;
214: return str;
215: }
216:
217: public boolean isPattern() {
218: return pattern;
219: }
220:
221: public String getCanonicalName() {
222: return canonicalName;
223:
224: }
225:
226: public String getDomain() {
227: return domain;
228: }
229:
230: public String getKeyProperty(String property) {
231: return (String) propertyMap.get(property);
232: }
233:
234: public Hashtable getKeyPropertyList() {
235: return new Hashtable(propertyMap);
236: }
237:
238: public String getKeyPropertyListString() {
239: return propertyListString;
240: }
241:
242: public String getCanonicalKeyPropertyListString() {
243: int i = domain.length() + 1;
244: try {
245: return canonicalName.substring(i, canonicalName.length());
246: } catch (Exception e) {
247: e.printStackTrace();
248: return "";
249: }
250: }
251:
252: public boolean isPropertyPattern() {
253: return propertyPattern;
254: }
255:
256: private void setPropertyPattern(boolean flag) {
257: propertyPattern = flag;
258: if (flag == true)
259: pattern = true;
260: }
261:
262: // add a key and value as a objectname property, will add to propertyMap and propertyListString
263: private void addProperty(String key, String value)
264: throws MalformedObjectNameException {
265: if (propertyMap.containsKey(key)) {
266: throw new MalformedObjectNameException("ObjectName: key `"
267: + key + "' already defined.");
268: }
269: propertyMap.put(key, value);
270: if (propertyListString.length() > 0) {
271: propertyListString += ",";
272: }
273: propertyListString += key + "=" + value;
274: }
275:
276: private static int parseKey(char[] charArray, int index)
277: throws MalformedObjectNameException {
278: int point = index;
279:
280: int length = charArray.length;
281:
282: while (point < length) {
283: char c = charArray[point++];
284: if (c == '*' || c == ',' || c == ':' || c == '?') {
285: throw new MalformedObjectNameException("ObjectName: `"
286: + c + "'" + " Invalid character in key");
287: }
288:
289: if (c == '=') {
290: point--;
291: break;
292: }
293: }
294:
295: return point;
296: }
297:
298: /**
299: *
300: * @param charArray , the char array of the propertyString
301: * @param index
302: * @return the end pointer of the value doStart with the index
303: * @throws MalformedObjectNameException
304: */
305: private static int parseValue(char[] charArray, int index)
306: throws MalformedObjectNameException {
307: int pointer = index;
308: int length = charArray.length;
309: char firstChar = charArray[index];
310: if (firstChar == '"') { // doStart with a quote
311: if (++pointer == length) // only one quote
312: throw new MalformedObjectNameException(
313: "ObjectName: Invalid quote.");
314: while (pointer < length) {
315: char c = charArray[pointer++];
316: if (c == '"')
317: break;
318: if (pointer == length) // find over, but can not find another quote
319: throw new MalformedObjectNameException(
320: "ObjectName: Invalid quote.");
321: }
322: if (pointer < length && charArray[pointer] != ',') // next not a ','
323: throw new MalformedObjectNameException(
324: "ObjectName: Invalid quote.");
325:
326: } else {
327: while (pointer < length) {
328: char c = charArray[pointer++];
329: if (c == '*' || c == ':' || c == '=' || c == '?') {
330: throw new MalformedObjectNameException(
331: "ObjectName: `" + c + "'"
332: + " Invalid character in value");
333: }
334: if (c == ',') { // a value end
335: pointer--;
336: break;
337: }
338: }
339: }
340: return pointer;
341: }
342:
343: private void construct(String name)
344: throws MalformedObjectNameException {
345: if (name.equals(""))
346: name = "*:*";
347: int indexOfColon = name.indexOf(":");
348: if (indexOfColon < 0) // not found ":"
349: throw new MalformedObjectNameException(
350: "ObjectName: domain part must be specified");
351: String domain = name.substring(0, indexOfColon);
352: setDomain(domain); // will check
353:
354: String propertyString = name.substring(indexOfColon + 1);
355: if (propertyString.length() == 0)
356: throw new MalformedObjectNameException(
357: "ObjectName: Key properties cannot be null");
358: char charArray[] = propertyString.toCharArray();
359: int length = charArray.length;
360:
361: int pointer = 0;
362: while (pointer < length) {
363: char c = charArray[pointer];
364: if (c == '*') {
365: if (++pointer != length) { // not the end
366: if (charArray[pointer] == ',') { // next is ','
367: if (++pointer == length)
368: throw new MalformedObjectNameException(
369: "ObjectName: Invalid comma");
370: } else {
371: throw new MalformedObjectNameException(
372: "ObjectName: Invalid pattern");
373: }
374: }
375: setPropertyPattern(true); // will also set pattern of the ObjectName to true
376: } else {
377: int endOfKey = parseKey(charArray, pointer);
378: if (endOfKey == pointer)
379: throw new MalformedObjectNameException(
380: "ObjectName: Invalid key (empty)");
381:
382: int startOfValue = endOfKey + 1;
383: if (startOfValue >= length) // can not find the value
384: throw new MalformedObjectNameException(
385: "ObjectName: Invalid property list format: no value.");
386:
387: int endOfValue = parseValue(charArray, startOfValue);
388:
389: if (endOfValue < length) { // not end
390: if (charArray[endOfValue] != ',') // not follow ","
391: throw new MalformedObjectNameException(
392: "ObjectName: `,' expected");
393: if (endOfValue + 1 >= length) // follow "," , but "," is end
394: throw new MalformedObjectNameException(
395: "ObjectName: Invalid comma");
396: }
397: if (endOfValue == startOfValue)
398: throw new MalformedObjectNameException(
399: "ObjectName: Invalid value (empty)");
400: String key = new String(charArray, pointer, endOfKey
401: - pointer);
402: String value = new String(charArray, startOfValue,
403: endOfValue - startOfValue);
404: addProperty(key, value);
405: pointer = endOfValue + 1;
406: }
407: }
408:
409: initCanonicalName();
410: }
411:
412: private void construct(String domain, Map table)
413: throws MalformedObjectNameException {
414: this .setDomain(domain);
415: Iterator iter = table.entrySet().iterator();
416: while (iter.hasNext()) {
417: Map.Entry entry = (Map.Entry) iter.next();
418: String key = (String) entry.getKey();
419: checkKey(key);
420: String value = (String) entry.getValue();
421: checkValue(value);
422: // will set propertyMap and propertyListString
423: addProperty(key, value);
424: }
425: initCanonicalName();
426: }
427:
428: private void setDomain(String domain)
429: throws MalformedObjectNameException {
430: checkDomain(domain);
431: this .domain = domain;
432: if (domain.indexOf("*") >= 0 || domain.indexOf("?") >= 0)
433: pattern = true;
434: }
435:
436: private void checkDomain(String domain)
437: throws MalformedObjectNameException {
438: if (domain.indexOf(',') >= 0 || domain.indexOf(':') > 0
439: || domain.indexOf('=') >= 0) {
440: throw new MalformedObjectNameException(
441: "ObjectName: Invalid domain -> " + domain);
442: }
443: }
444:
445: private String checkValue(String value)
446: throws MalformedObjectNameException {
447: if (value == null || value.length() == 0)
448: throw new MalformedObjectNameException(
449: "ObjectName: value can not be null or empty.");
450:
451: char charArray[] = value.toCharArray();
452: int length = charArray.length;
453: int point = parseValue(charArray, 0);
454: if (point < length)
455: throw new MalformedObjectNameException("ObjectName: `"
456: + charArray[point] + "'"
457: + " Invalid character in value");
458: return value;
459: }
460:
461: private String checkKey(String key)
462: throws MalformedObjectNameException {
463: if (key == null || key.length() == 0)
464: throw new MalformedObjectNameException(
465: "ObjectName: key can not be null or empty.");
466: char charArray[] = key.toCharArray();
467: int j = parseKey(charArray, 0);
468: if (j < key.length())
469: throw new MalformedObjectNameException("ObjectName: `"
470: + charArray[j] + "'"
471: + " Invalid character in value");
472:
473: return key;
474: }
475:
476: private void readObject(ObjectInputStream in) throws IOException,
477: ClassNotFoundException {
478: in.defaultReadObject();
479: String objectNameString = (String) in.readObject();
480: canonicalName = "";
481: propertyMap = new HashMap();
482: propertyListString = "";
483: try {
484: construct(objectNameString);
485: } catch (MalformedObjectNameException e) {
486: throw new InvalidObjectException(e.toString());
487: }
488: }
489:
490: private void writeObject(ObjectOutputStream out) throws IOException {
491: out.defaultWriteObject();
492: String objectNameString = domain + ":" + propertyListString;
493: if (propertyPattern) {
494: if (propertyMap.isEmpty())
495: objectNameString = objectNameString + "*";
496: else
497: objectNameString = objectNameString + ",*";
498: }
499: out.writeObject(objectNameString);
500:
501: }
502:
503: private void initCanonicalName() {
504: // if(canonicalName == null || canonicalName.length() == 0){ // initialize
505: StringBuffer sb = new StringBuffer();
506: sb.append(domain);
507: sb.append(':');
508: if (propertyMap.isEmpty())
509: sb.append("*");
510:
511: if (propertyMap.size() == 1) {
512: sb.append(propertyListString);
513: } else {
514: List list = new ArrayList();
515: Iterator iter = propertyMap.entrySet().iterator();
516: while (iter.hasNext()) {
517: Map.Entry entry = (Map.Entry) iter.next();
518: String key = (String) entry.getKey();
519: String value = (String) entry.getValue();
520: String property = key + "="
521: + (value != null ? value : "");
522: list.add(property);
523: }
524:
525: String[] propertyArray = (String[]) list
526: .toArray(new String[0]);
527: Arrays.sort(propertyArray); // sort it
528: int i = 0;
529: int length = propertyArray.length;
530: while (i < length) {
531: sb.append(propertyArray[i]);
532: if (++i < length)
533: sb.append(',');
534: }
535: }
536: canonicalName = sb.toString();
537: // }
538:
539: }
540:
541: public boolean apply(ObjectName name)
542: throws BadStringOperationException,
543: BadBinaryOpValueExpException,
544: BadAttributeValueExpException, InvalidApplicationException {
545: if (name == null)
546: throw new NullPointerException();
547:
548: if (name.pattern || name.propertyPattern)
549: return false;
550:
551: // No pattern
552: if (!pattern && !propertyPattern)
553: return canonicalName.equals(name.canonicalName);
554:
555: return matchDomains(name) && matchKeys(name);
556:
557: }
558:
559: private final boolean matchDomains(ObjectName name) {
560: if (pattern) {
561: // wildmatch domains
562: final char[] dom_pattern = getDomain().toCharArray();
563: final char[] dom_string = name.getDomain().toCharArray();
564: return wildmatch(dom_string, dom_pattern, 0, 0);
565: }
566: return getDomain().equals(name.getDomain());
567: }
568:
569: /*
570: * Tests whether string s is matched by pattern p.
571: * Supports "?", "*" each of which may be escaped with "\";
572: * Not yet supported: internationalization; "\" inside brackets.<P>
573: * Wildcard matching routine by Karl Heuer. Public Domain.<P>
574: */
575: private static boolean wildmatch(char[] s, char[] p, int si, int pi) {
576: char c;
577: final int slen = s.length;
578: final int plen = p.length;
579:
580: while (pi < plen) { // While still string
581: c = p[pi++];
582: if (c == '?') {
583: if (++si > slen)
584: return false;
585: } else if (c == '*') { // Wildcard
586: if (pi >= plen)
587: return true;
588: do {
589: if (wildmatch(s, p, si, pi))
590: return true;
591: } while (++si < slen);
592: return false;
593: } else {
594: if (si >= slen || c != s[si++])
595: return false;
596: }
597: }
598: return (si == slen);
599: }
600:
601: private boolean matchKeys(ObjectName objectName) {
602: Hashtable nameTable = objectName.getKeyPropertyList();
603: Hashtable patternTable = this .getKeyPropertyList();
604: boolean matches = true;
605: if (!this .isPropertyPattern()) { // not pattern,must be equal
606: matches = nameTable.equals(patternTable);
607: } else { // is pattern, so nameTable must containsAll patterTable
608: Iterator it = patternTable.entrySet().iterator();
609: while (it.hasNext()) {
610: Map.Entry entry = (Map.Entry) it.next();
611: String key = (String) entry.getKey();
612: String value = (String) entry.getValue();
613: if (!nameTable.containsKey(key)
614: || !nameTable.get(key).equals(value)) {
615: matches = false;
616: break;
617: }
618: }
619: }
620:
621: return matches;
622: }
623:
624: public void setMBeanServer(MBeanServer server) {
625:
626: }
627:
628: public static void main(String[] args) throws Exception {
629: ObjectName on = new ObjectName("DefaultDomain", "name", "young");
630: System.out.println(on.getKeyPropertyListString());
631:
632: on = new ObjectName("", "name", "young");
633: System.out.println(on.getDomain());
634:
635: Hashtable table = new Hashtable();
636: table.put("name", "young");
637: table.put("age", "20");
638: on = new ObjectName("DefaultDomain", table);
639: System.out.println(on.getCanonicalKeyPropertyListString());
640:
641: on = new ObjectName(":name=young,age=\"20\",*");
642: System.out.println(on.isPattern() + ", "
643: + on.isPropertyPattern());
644:
645: on = new ObjectName("*:name=young,age=20");
646: System.out.println(on.isPattern() + ", "
647: + on.isPropertyPattern());
648:
649: }
650:
651: }
|