001: /*
002: * Copyright (C) The MX4J Contributors.
003: * All rights reserved.
004: *
005: * This software is distributed under the terms of the MX4J License version 1.0.
006: * See the terms of the MX4J License in the documentation provided with this software.
007: */
008:
009: package javax.management;
010:
011: import java.io.IOException;
012: import java.io.InvalidObjectException;
013: import java.io.ObjectInputStream;
014: import java.io.ObjectOutputStream;
015: import java.io.Serializable;
016: import java.lang.ref.ReferenceQueue;
017: import java.lang.ref.WeakReference;
018: import java.security.AccessController;
019: import java.security.PrivilegedAction;
020: import java.util.HashMap;
021: import java.util.Hashtable;
022: import java.util.Iterator;
023: import java.util.Map;
024: import java.util.TreeMap;
025:
026: import mx4j.MX4JSystemKeys;
027: import mx4j.util.Utils;
028:
029: /**
030: * @version $Revision: 1.25 $
031: */
032: public class ObjectName implements QueryExp, Serializable {
033: private static final long serialVersionUID = 1081892073854801359L;
034:
035: private static final boolean cacheEnabled;
036: private static final WeakObjectNameCache cache;
037:
038: static {
039: String enableCache = (String) AccessController
040: .doPrivileged(new PrivilegedAction() {
041: public Object run() {
042: return System
043: .getProperty(MX4JSystemKeys.MX4J_OBJECTNAME_CACHING);
044: }
045: });
046: if (enableCache != null) {
047: cacheEnabled = Boolean.valueOf(enableCache).booleanValue();
048: } else {
049: // Cache is on by default
050: cacheEnabled = true;
051: }
052: if (cacheEnabled) {
053: cache = new WeakObjectNameCache();
054: } else {
055: cache = null;
056: }
057: }
058:
059: private transient String propertiesString;
060: private transient boolean isPropertyPattern;
061: private transient boolean isDomainPattern;
062: private transient String canonicalName;
063:
064: public ObjectName(String name) throws MalformedObjectNameException {
065: if (name == null)
066: throw new NullPointerException(
067: "ObjectName 'name' parameter can't be null");
068: if (name.length() == 0)
069: name = "*:*";
070: parse(name);
071: }
072:
073: public ObjectName(String domain, Hashtable table)
074: throws MalformedObjectNameException {
075: if (domain == null)
076: throw new NullPointerException(
077: "ObjectName 'domain' parameter can't be null");
078: if (table == null)
079: throw new NullPointerException(
080: "ObjectName 'table' parameter can't be null");
081: if (!isDomainValid(domain))
082: throw new MalformedObjectNameException("Invalid domain: "
083: + domain);
084: if (table.isEmpty())
085: throw new MalformedObjectNameException(
086: "Properties table cannot be empty");
087:
088: for (Iterator i = table.entrySet().iterator(); i.hasNext();) {
089: Map.Entry entry = (Map.Entry) i.next();
090: String key = entry.getKey().toString();
091: if (!isKeyValid(key))
092: throw new MalformedObjectNameException("Invalid key: "
093: + key);
094: Object value = entry.getValue();
095: if (!(value instanceof String))
096: throw new MalformedObjectNameException(
097: "Property values must be Strings");
098: String strvalue = value.toString();
099: if (!isValueValid(strvalue))
100: throw new MalformedObjectNameException(
101: "Invalid value: " + strvalue);
102: }
103:
104: init(domain, convertPropertiesToString(new TreeMap(table)),
105: table);
106: }
107:
108: public ObjectName(String domain, String key, String value)
109: throws MalformedObjectNameException {
110: if (domain == null)
111: throw new NullPointerException(
112: "ObjectName 'domain' parameter can't be null");
113: if (key == null)
114: throw new NullPointerException(
115: "ObjectName 'key' parameter can't be null");
116: if (value == null)
117: throw new NullPointerException(
118: "ObjectName 'value' parameter can't be null");
119: if (!isDomainValid(domain))
120: throw new MalformedObjectNameException("Invalid domain: "
121: + domain);
122: if (!isKeyValid(key))
123: throw new MalformedObjectNameException("Invalid key: "
124: + key);
125: if (!isValueValid(value))
126: throw new MalformedObjectNameException("Invalid value: "
127: + value);
128:
129: Map table = new HashMap();
130: table.put(key, value);
131: init(domain, convertPropertiesToString(table), table);
132: }
133:
134: public boolean apply(ObjectName name) {
135: boolean result = false;
136:
137: if (name.isPattern())
138: result = false;
139: else if (isPattern())
140: result = domainsMatch(this , name)
141: && propertiesMatch(this , name);
142: else
143: result = equals(name);
144:
145: return result;
146: }
147:
148: boolean implies(ObjectName name) {
149: return domainsMatch(this , name) && propertiesMatch(this , name);
150: }
151:
152: private boolean domainsMatch(ObjectName name1, ObjectName name2) {
153: String this Domain = name1.getDomain();
154: boolean this Pattern = name1.isDomainPattern();
155: String otherDomain = name2.getDomain();
156: boolean otherPattern = name2.isDomainPattern();
157:
158: if (!this Pattern && otherPattern)
159: return false;
160: if (!this Pattern && !otherPattern
161: && !this Domain.equals(otherDomain))
162: return false;
163: return Utils.wildcardMatch(this Domain, otherDomain);
164: }
165:
166: private boolean propertiesMatch(ObjectName name1, ObjectName name2) {
167: Map this Properties = name1.getPropertiesMap();
168: boolean this Pattern = name1.isPropertyPattern();
169: Map otherProperties = name2.getPropertiesMap();
170: boolean otherPattern = name2.isPropertyPattern();
171:
172: if (!this Pattern && otherPattern)
173: return false;
174: if (!this Pattern && !otherPattern
175: && !this Properties.equals(otherProperties))
176: return false;
177: if (this Pattern
178: && !otherProperties.entrySet().containsAll(
179: this Properties.entrySet()))
180: return false;
181:
182: return true;
183: }
184:
185: public void setMBeanServer(MBeanServer server) {
186: }
187:
188: public String getCanonicalKeyPropertyListString() {
189: String canonical = getCanonicalName();
190: int index = canonical.indexOf(':');
191: String list = canonical.substring(index + 1);
192: if (isPropertyPattern()) {
193: if (getKeyPropertyListString().length() == 0)
194: return list.substring(0, list.length() - "*".length());
195: else
196: return list.substring(0, list.length() - ",*".length());
197: }
198: return list;
199: }
200:
201: public String getCanonicalName() {
202: return canonicalName;
203: }
204:
205: public String getDomain() {
206: String canonical = getCanonicalName();
207: int index = canonical.indexOf(':');
208: return canonical.substring(0, index);
209: }
210:
211: public String getKeyProperty(String key) {
212: Map props = getPropertiesMap();
213: return (String) props.get(key);
214: }
215:
216: public Hashtable getKeyPropertyList() {
217: return new Hashtable(getPropertiesMap());
218: }
219:
220: private Map getPropertiesMap() {
221: // TODO: Consider to cache this Map
222: try {
223: return convertStringToProperties(
224: getKeyPropertyListString(), null);
225: } catch (MalformedObjectNameException x) {
226: return null;
227: }
228: }
229:
230: public String getKeyPropertyListString() {
231: return propertiesString;
232: }
233:
234: public boolean isPattern() {
235: return isDomainPattern() || isPropertyPattern();
236: }
237:
238: public boolean isPropertyPattern() {
239: return isPropertyPattern;
240: }
241:
242: public boolean isDomainPattern() {
243: return isDomainPattern;
244: }
245:
246: public static ObjectName getInstance(ObjectName name) {
247: if (name.getClass() == ObjectName.class)
248: return name;
249:
250: try {
251: return getInstance(name.getCanonicalName());
252: } catch (MalformedObjectNameException x) {
253: throw new IllegalArgumentException(x.toString());
254: }
255: }
256:
257: public static ObjectName getInstance(String name)
258: throws MalformedObjectNameException {
259: if (cacheEnabled) {
260: ObjectName cached = null;
261: synchronized (cache) {
262: cached = cache.get(name);
263: }
264: if (cached != null)
265: return cached;
266: }
267:
268: // Keep ObjectName creation, that takes time for parsing, outside the synchronized block.
269: return new ObjectName(name);
270: }
271:
272: public static ObjectName getInstance(String domain, Hashtable table)
273: throws MalformedObjectNameException {
274: return new ObjectName(domain, table);
275: }
276:
277: public static ObjectName getInstance(String domain, String key,
278: String value) throws MalformedObjectNameException {
279: return new ObjectName(domain, key, value);
280: }
281:
282: public static String quote(String value) {
283: StringBuffer buffer = new StringBuffer("\"");
284: for (int i = 0; i < value.length(); ++i) {
285: char ch = value.charAt(i);
286: switch (ch) {
287: case '\n':
288: buffer.append("\\n");
289: break;
290: case '\"':
291: buffer.append("\\\"");
292: break;
293: case '\\':
294: buffer.append("\\\\");
295: break;
296: case '*':
297: buffer.append("\\*");
298: break;
299: case '?':
300: buffer.append("\\?");
301: break;
302: default:
303: buffer.append(ch);
304: break;
305: }
306: }
307: buffer.append("\"");
308: return buffer.toString();
309: }
310:
311: public static String unquote(String value)
312: throws IllegalArgumentException {
313: int lastIndex = value.length() - 1;
314: if (lastIndex < 1 || value.charAt(0) != '\"'
315: || value.charAt(lastIndex) != '\"')
316: throw new IllegalArgumentException(
317: "The given string is not quoted");
318:
319: StringBuffer buffer = new StringBuffer();
320: for (int i = 1; i < lastIndex; ++i) {
321: char ch = value.charAt(i);
322: if (ch == '\\') {
323: // Found a backslash, let's see if it marks an escape sequence
324: ++i;
325: if (i == lastIndex)
326: throw new IllegalArgumentException(
327: "Invalid escape sequence at the end of quoted string");
328: ch = value.charAt(i);
329: switch (ch) {
330: case 'n':
331: buffer.append("\n");
332: break;
333: case '\"':
334: buffer.append("\"");
335: break;
336: case '\\':
337: buffer.append("\\");
338: break;
339: case '*':
340: buffer.append("*");
341: break;
342: case '?':
343: buffer.append("?");
344: break;
345: default:
346: throw new IllegalArgumentException(
347: "Invalid escape sequence: \\" + ch);
348: }
349: } else {
350: switch (ch) {
351: case '\n':
352: case '\"':
353: case '*':
354: case '?':
355: throw new IllegalArgumentException(
356: "Invalid unescaped character: " + ch);
357: default:
358: buffer.append(ch);
359: }
360: }
361: }
362: return buffer.toString();
363: }
364:
365: private void parse(String name) throws MalformedObjectNameException {
366: boolean isSubclass = getClass() != ObjectName.class;
367:
368: // It is important from the security point of view to not cache subclasses.
369: // An EvilObjectName may return an allowed domain when security checks are made, and
370: // a prohibited domain when performing operations. Here we make sure that subclasses
371: // are not cached.
372: if (cacheEnabled && !isSubclass) {
373: ObjectName cached = null;
374: synchronized (cache) {
375: cached = cache.get(name);
376: }
377: if (cached != null) {
378: // This ObjectName is already created, just copy it to avoid string parsing
379: propertiesString = cached.getKeyPropertyListString();
380: isDomainPattern = cached.isDomainPattern();
381: isPropertyPattern = cached.isPropertyPattern();
382: canonicalName = cached.getCanonicalName();
383: return;
384: }
385: }
386:
387: String domain = parseDomain(name);
388: if (!isDomainValid(domain))
389: throw new MalformedObjectNameException("Invalid domain: "
390: + domain);
391:
392: // Properties must be handled carefully.
393: // The main problem is to create the keyPropertiesListString for non trivial cases such as
394: // 1. no properties
395: // 2. presence of the '*' wildcard in the middle of the list
396: // 3. quoted values that contain the '*' wildcard
397: // while maintaining the properties' order
398: String properties = parseProperties(name);
399: // Preliminar, easy checks
400: if (properties.trim().length() < 1)
401: throw new MalformedObjectNameException("Missing properties");
402: if (properties.trim().endsWith(","))
403: throw new MalformedObjectNameException(
404: "Missing property after trailing comma");
405: StringBuffer propsString = new StringBuffer();
406: Map table = convertStringToProperties(properties, propsString);
407:
408: init(domain, propsString.toString(), table);
409:
410: if (cacheEnabled && !isSubclass) {
411: // Cache this ObjectName
412: synchronized (cache) {
413: // Overwrite if 2 threads computed the same ObjectName: we have been unlucky
414: cache.put(name, this );
415: }
416: }
417: }
418:
419: private String parseDomain(String objectName)
420: throws MalformedObjectNameException {
421: int colon = objectName.indexOf(':');
422: if (colon < 0)
423: throw new MalformedObjectNameException(
424: "Missing ':' character in ObjectName");
425:
426: String domain = objectName.substring(0, colon);
427: return domain;
428: }
429:
430: private boolean isDomainValid(String domain) {
431: if (domain == null)
432: return false;
433: if (domain.indexOf('\n') >= 0)
434: return false;
435: if (domain.indexOf(":") >= 0)
436: return false;
437: return true;
438: }
439:
440: private String parseProperties(String objectName)
441: throws MalformedObjectNameException {
442: int colon = objectName.indexOf(':');
443: if (colon < 0)
444: throw new MalformedObjectNameException(
445: "Missing ':' character in ObjectName");
446:
447: String list = objectName.substring(colon + 1);
448: return list;
449: }
450:
451: /**
452: * Returns a Map containing the pairs (key,value) parsed from the given string.
453: * If the given string contains the wildcard '*', then the returned Hashtable will contains the pair (*,*).
454: * If the given StringBuffer is not null, it will be filled with the
455: * {@link #getKeyPropertyListString keyPropertiesListString}.
456: *
457: * @see #initProperties
458: */
459: private Map convertStringToProperties(String properties,
460: StringBuffer buffer) throws MalformedObjectNameException {
461: if (buffer != null)
462: buffer.setLength(0);
463: Map table = new HashMap();
464:
465: StringBuffer toBeParsed = new StringBuffer(properties);
466: while (toBeParsed.length() > 0) {
467: String key = parsePropertyKey(toBeParsed);
468:
469: String value = null;
470: if ("*".equals(key))
471: value = "*";
472: else
473: value = parsePropertyValue(toBeParsed);
474:
475: Object duplicate = table.put(key, value);
476: if (duplicate != null)
477: throw new MalformedObjectNameException(
478: "Duplicate key not allowed: " + key);
479:
480: if (buffer != null && !"*".equals(key)) {
481: if (buffer.length() > 0)
482: buffer.append(',');
483: buffer.append(key).append('=').append(value);
484: }
485: }
486:
487: return table;
488: }
489:
490: private String parsePropertyKey(StringBuffer buffer)
491: throws MalformedObjectNameException {
492: String toBeParsed = buffer.toString();
493: int equal = toBeParsed.indexOf('=');
494: int comma = toBeParsed.indexOf(',');
495:
496: if (equal < 0 && comma < 0) {
497: // Then it can only be the asterisk
498: String key = toBeParsed.trim();
499: if (!"*".equals(key))
500: throw new MalformedObjectNameException("Invalid key: '"
501: + key + "'");
502: buffer.setLength(0);
503: return key;
504: }
505:
506: if (comma >= 0 && comma < equal) {
507: // Then it can only be the asterisk
508: String key = toBeParsed.substring(0, comma).trim();
509: if (!"*".equals(key))
510: throw new MalformedObjectNameException("Invalid key: '"
511: + key + "'");
512: buffer.delete(0, comma + 1);
513: return key;
514: }
515:
516: // Normal key
517: String key = toBeParsed.substring(0, equal);
518: if (!isKeyValid(key))
519: throw new MalformedObjectNameException("Invalid key: '"
520: + key + "'");
521: buffer.delete(0, equal + 1);
522: return key;
523: }
524:
525: private boolean isKeyValid(String key) {
526: if (key == null)
527: return false;
528: if (key.trim().length() < 1)
529: return false;
530: if (key.indexOf('\n') >= 0)
531: return false;
532: if (key.indexOf(',') >= 0)
533: return false;
534: if (key.indexOf('=') >= 0)
535: return false;
536: if (key.indexOf('*') >= 0)
537: return false;
538: if (key.indexOf('?') >= 0)
539: return false;
540: if (key.indexOf(':') >= 0)
541: return false;
542: return true;
543: }
544:
545: private String parsePropertyValue(StringBuffer buffer)
546: throws MalformedObjectNameException {
547: String toBeParsed = buffer.toString();
548: if (toBeParsed.trim().startsWith("\"")) {
549: // It's quoted, delimiter is the closing quote
550: int start = toBeParsed.indexOf('"') + 1;
551: int endQuote = -1;
552:
553: while ((endQuote = toBeParsed.indexOf('"', start)) >= 0) {
554: int bslashes = countBackslashesBackwards(toBeParsed,
555: endQuote);
556: if (bslashes % 2 != 0) {
557: start = endQuote + 1;
558: continue;
559: }
560:
561: // Found closing quote
562: String value = toBeParsed.substring(0, endQuote + 1)
563: .trim();
564: if (!isValueValid(value))
565: throw new MalformedObjectNameException(
566: "Invalid value: '" + value + "'");
567:
568: buffer.delete(0, endQuote + 1);
569: // Remove also a possible trailing comma
570: toBeParsed = buffer.toString();
571: if (toBeParsed.trim().startsWith(",")) {
572: int comma = toBeParsed.indexOf(',');
573: buffer.delete(0, comma + 1);
574: return value;
575: } else if (toBeParsed.trim().length() == 0) {
576: buffer.setLength(0);
577: return value;
578: } else {
579: throw new MalformedObjectNameException(
580: "Garbage after quoted value: " + toBeParsed);
581: }
582: }
583: throw new MalformedObjectNameException(
584: "Missing closing quote: " + toBeParsed);
585: } else {
586: // Non quoted, delimiter is comma
587: int comma = toBeParsed.indexOf(',');
588: if (comma >= 0) {
589: String value = toBeParsed.substring(0, comma);
590: if (!isValueValid(value))
591: throw new MalformedObjectNameException(
592: "Invalid value: '" + value + "'");
593: buffer.delete(0, comma + 1);
594: return value;
595: } else {
596: String value = toBeParsed;
597: if (!isValueValid(value))
598: throw new MalformedObjectNameException(
599: "Invalid value: '" + value + "'");
600: buffer.setLength(0);
601: return value;
602: }
603: }
604: }
605:
606: private int indexOfLastConsecutiveBackslash(String value, int from) {
607: int index = value.indexOf('\\', from);
608: if (index < 0)
609: return index;
610: if (index == value.length() - 1)
611: return index;
612: // Probe next char
613: int next = indexOfLastConsecutiveBackslash(value, from + 1);
614: if (next < 0)
615: return index;
616: else
617: return next;
618: }
619:
620: private boolean isValueValid(String value) {
621: if (value == null)
622: return false;
623: if (value.length() == 0)
624: return false;
625: if (value.indexOf('\n') >= 0)
626: return false;
627:
628: if (value.trim().startsWith("\"")) {
629: // strip leading and trailing spaces
630: value = value.trim();
631:
632: // check value has quotes at start and end
633: if (value.length() < 2)
634: return false;
635: if (value.charAt(value.length() - 1) != '"')
636: return false;
637:
638: // check final quote is not escaped
639: if (countBackslashesBackwards(value, value.length() - 1) % 2 == 1)
640: return false;
641:
642: // Unquote the value
643: value = value.substring(1, value.length() - 1);
644:
645: // Be sure escaped values are interpreted correctly
646: int start = 0;
647: int index = -1;
648: do {
649: index = indexOfLastConsecutiveBackslash(value, start);
650: if (index >= 0) {
651: // Found a backslash sequence, see if it's an escape or a backslash
652: int count = countBackslashesBackwards(value,
653: index + 1);
654: if (count % 2 != 0) {
655: // Odd number of backslashes, probe next character, should be either '\', 'n', '"', '?', '*'
656: if (index == value.length() - 1)
657: return false;
658:
659: char next = value.charAt(index + 1);
660: if (next != '\\' && next != 'n' && next != '"'
661: && next != '?' && next != '*')
662: return false;
663: }
664: start = index + 1;
665: }
666: } while (index >= 0);
667:
668: start = 0;
669: index = -1;
670: do {
671: index = value.indexOf('"', start);
672: if (index < 0)
673: index = value.indexOf('*', start);
674: if (index < 0)
675: index = value.indexOf('?', start);
676: if (index >= 0) {
677: int bslashCount = countBackslashesBackwards(value,
678: index);
679: // There is a special character not preceded by an odd number of backslashes
680: if (bslashCount % 2 == 0)
681: return false;
682: start = index + 1;
683: }
684: } while (index >= 0);
685: } else {
686: if (value.indexOf(',') >= 0)
687: return false;
688: if (value.indexOf('=') >= 0)
689: return false;
690: if (value.indexOf(':') >= 0)
691: return false;
692: if (value.indexOf('"') >= 0)
693: return false;
694: if (value.indexOf('*') >= 0)
695: return false;
696: if (value.indexOf('?') >= 0)
697: return false;
698: }
699: return true;
700: }
701:
702: private int countBackslashesBackwards(String string, int from) {
703: int bslashCount = 0;
704: while (--from >= 0) {
705: if (string.charAt(from) == '\\')
706: ++bslashCount;
707: else
708: break;
709: }
710: return bslashCount;
711: }
712:
713: /**
714: * Initializes this ObjectName with the given domain, propertiesString and properties.
715: *
716: * @see #convertStringToProperties
717: */
718: private void init(String domain, String propertiesString,
719: Map properties) {
720: initDomain(domain);
721: initProperties(properties);
722: this .propertiesString = propertiesString;
723: StringBuffer buffer = new StringBuffer(domain).append(':')
724: .append(
725: convertPropertiesToString(new TreeMap(
726: properties)));
727: if (isPropertyPattern()) {
728: if (getKeyPropertyListString().length() == 0)
729: buffer.append('*');
730: else
731: buffer.append(",*");
732: }
733: canonicalName = buffer.toString();
734: }
735:
736: /**
737: * If the given domain contains the '*' or the '?' characters, sets this ObjectName as a domain pattern.
738: */
739: private void initDomain(String domain) {
740: // Domain may contain '*' and '?' characters if it's a pattern
741: if (domain.indexOf('*') >= 0 || domain.indexOf('?') >= 0) {
742: isDomainPattern = true;
743: }
744: }
745:
746: /**
747: * If present, it removes the pair (*,*) from the given Hashtable, and sets this ObjectName as a property pattern.
748: *
749: * @see #convertStringToProperties
750: */
751: private void initProperties(Map properties) {
752: if (properties.containsKey("*")) {
753: // The Hashtable will never contain the '*'
754: properties.remove("*");
755: isPropertyPattern = true;
756: }
757: }
758:
759: /**
760: * Converts the pairs present in the given Map into a comma separated list of tokens
761: * with the form 'key=value'
762: */
763: private String convertPropertiesToString(Map properties) {
764: StringBuffer b = new StringBuffer();
765: boolean firstTime = true;
766: for (Iterator i = properties.entrySet().iterator(); i.hasNext();) {
767: if (!firstTime)
768: b.append(",");
769: else
770: firstTime = false;
771:
772: Map.Entry entry = (Map.Entry) i.next();
773: b.append(entry.getKey());
774: b.append("=");
775: b.append(entry.getValue());
776: }
777:
778: return b.toString();
779: }
780:
781: public int hashCode() {
782: return getCanonicalName().hashCode();
783: }
784:
785: public boolean equals(Object obj) {
786: if (obj == null)
787: return false;
788: if (obj == this )
789: return true;
790:
791: try {
792: ObjectName other = (ObjectName) obj;
793: return getCanonicalName().equals(other.getCanonicalName());
794: } catch (ClassCastException ignored) {
795: }
796: return false;
797: }
798:
799: public String toString() {
800: return getName(false);
801: }
802:
803: private String getName(boolean canonical) {
804: // TODO: Remove the boolean argument, not used anymore
805: StringBuffer buffer = new StringBuffer(getDomain()).append(':');
806: String properties = canonical ? getCanonicalKeyPropertyListString()
807: : getKeyPropertyListString();
808: buffer.append(properties);
809: if (isPropertyPattern()) {
810: if (properties.length() == 0)
811: buffer.append("*");
812: else
813: buffer.append(",*");
814: }
815: return buffer.toString();
816: }
817:
818: private void writeObject(ObjectOutputStream out) throws IOException {
819: out.defaultWriteObject();
820: String name = getName(false);
821: out.writeObject(name);
822: }
823:
824: private void readObject(ObjectInputStream in) throws IOException,
825: ClassNotFoundException {
826: in.defaultReadObject();
827: String objectName = (String) in.readObject();
828: try {
829: parse(objectName);
830: } catch (MalformedObjectNameException x) {
831: throw new InvalidObjectException(
832: "String representing the ObjectName is not a valid ObjectName: "
833: + x.toString());
834: }
835: }
836:
837: private static class WeakObjectNameCache {
838: private ReferenceQueue queue = new ReferenceQueue();
839: private HashMap map = new HashMap();
840:
841: public void put(String key, ObjectName value) {
842: cleanup();
843: map.put(key, WeakValue.create(key, value, queue));
844: }
845:
846: public ObjectName get(String key) {
847: cleanup();
848: WeakValue value = (WeakValue) map.get(key);
849: if (value == null)
850: return null;
851: else
852: return (ObjectName) value.get();
853: }
854:
855: private void cleanup() {
856: WeakValue ref = null;
857: while ((ref = (WeakValue) queue.poll()) != null) {
858: map.remove(ref.getKey());
859: }
860: }
861:
862: private static final class WeakValue extends WeakReference {
863: private Object key;
864:
865: /**
866: * Creates a new WeakValue
867: *
868: * @return null if the given value is null.
869: */
870: public static WeakValue create(Object key, Object value,
871: ReferenceQueue queue) {
872: if (value == null)
873: return null;
874: return new WeakValue(key, value, queue);
875: }
876:
877: private WeakValue(Object key, Object value,
878: ReferenceQueue queue) {
879: super (value, queue);
880: this .key = key;
881: }
882:
883: public Object getKey() {
884: return key;
885: }
886: }
887: }
888: }
|