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.modelmbean;
010:
011: import java.io.ByteArrayInputStream;
012: import java.io.IOException;
013: import java.io.ObjectInputStream;
014: import java.io.ObjectOutputStream;
015: import java.util.HashMap;
016: import java.util.Iterator;
017: import java.util.Map;
018: import javax.management.Descriptor;
019: import javax.management.MBeanException;
020: import javax.management.RuntimeOperationsException;
021: import javax.xml.parsers.DocumentBuilder;
022: import javax.xml.parsers.DocumentBuilderFactory;
023:
024: import org.w3c.dom.DOMException;
025: import org.w3c.dom.Document;
026: import org.w3c.dom.Element;
027: import org.w3c.dom.NamedNodeMap;
028: import org.w3c.dom.Node;
029: import org.w3c.dom.NodeList;
030:
031: /**
032: * @version $Revision: 1.30 $
033: */
034: public class DescriptorSupport implements Descriptor {
035: private static final long serialVersionUID = -6292969195866300415L;
036:
037: private HashMap descriptor;
038: private transient HashMap fields = new HashMap(20);
039:
040: public DescriptorSupport() {
041: }
042:
043: public DescriptorSupport(int initNumFields) throws MBeanException,
044: RuntimeOperationsException {
045: if (initNumFields <= 0) {
046: throw new RuntimeOperationsException(
047: new IllegalArgumentException(
048: "Number of Fields cannot be <= 0"));
049: }
050: fields = new HashMap(initNumFields);
051: }
052:
053: public DescriptorSupport(DescriptorSupport inDescr) {
054: if (inDescr != null) {
055: setFields(inDescr.getFieldNames(), inDescr
056: .getFieldValues(inDescr.getFieldNames()));
057: }
058: }
059:
060: public DescriptorSupport(String xml) throws MBeanException,
061: RuntimeOperationsException, XMLParseException {
062: if (xml == null) {
063: throw new RuntimeOperationsException(
064: new IllegalArgumentException(
065: "Descriptor XML string is null"));
066: }
067:
068: NodeList fields = documentFromXML(xml).getElementsByTagName(
069: "field");
070: for (int i = 0; i < fields.getLength(); i++) {
071: addFieldFromXML(fields.item(i));
072: }
073: }
074:
075: public DescriptorSupport(String[] pairs) {
076: if (pairs != null && pairs.length != 0) {
077: for (int i = 0; i < pairs.length; ++i) {
078: String pair = pairs[i];
079: // null or empty strings are to be ignored
080: if (pair == null || pair.length() == 0)
081: continue;
082:
083: int equal = pair.indexOf('=');
084: if (equal < 1) {
085: throw new RuntimeOperationsException(
086: new IllegalArgumentException(
087: "Illegal pair: " + pair));
088: } else {
089: String name = pair.substring(0, equal);
090: Object value = null;
091: if (equal < pair.length() - 1) {
092: value = pair.substring(equal + 1);
093: }
094: setField(name, value);
095: }
096: }
097: }
098: }
099:
100: public DescriptorSupport(String[] names, Object[] values) {
101: setFields(names, values);
102: }
103:
104: public Object clone() throws RuntimeOperationsException {
105: return new DescriptorSupport(this );
106: }
107:
108: public Object getFieldValue(String name)
109: throws RuntimeOperationsException {
110: if (name == null || name.trim().length() == 0) {
111: throw new RuntimeOperationsException(
112: new IllegalArgumentException("Invalid field name"));
113: }
114: // Field names are case insensitive, retrieve the value from the case-insensitive map
115: ValueHolder holder = (ValueHolder) fields.get(name
116: .toLowerCase());
117: return holder == null ? null : holder.fieldValue;
118: }
119:
120: public void setField(String name, Object value)
121: throws RuntimeOperationsException {
122: checkField(name, value);
123:
124: // update field but keep the original name if an entry already exists
125: String lcase = name.toLowerCase();
126: ValueHolder holder = (ValueHolder) fields.get(lcase);
127: ValueHolder newHolder = new ValueHolder(holder == null ? name
128: : holder.fieldName, value);
129: fields.put(lcase, newHolder);
130: }
131:
132: public void removeField(String name) {
133: if (name != null) {
134: fields.remove(name.toLowerCase());
135: }
136: }
137:
138: public String[] getFieldNames() {
139: // Preserve the case of field names so use the ones from the values
140: String[] names = new String[fields.size()];
141: int x = 0;
142: for (Iterator i = fields.values().iterator(); i.hasNext();) {
143: ValueHolder holder = (ValueHolder) i.next();
144: names[x++] = holder.fieldName;
145: }
146: return names;
147: }
148:
149: public Object[] getFieldValues(String[] names) {
150: // quick check for empty descriptor (which overrides all)
151: if (fields.isEmpty())
152: return new Object[0];
153:
154: if (names == null) {
155: // All values must be returned
156: Object[] list = new Object[fields.size()];
157: int x = 0;
158: for (Iterator i = fields.values().iterator(); i.hasNext();) {
159: ValueHolder holder = (ValueHolder) i.next();
160: list[x++] = holder.fieldValue;
161: }
162: return list;
163: }
164:
165: Object[] list = new Object[names.length];
166: for (int i = 0; i < names.length; ++i) {
167: try {
168: list[i] = getFieldValue(names[i]);
169: } catch (RuntimeOperationsException x) {
170: list[i] = null;
171: }
172: }
173: return list;
174: }
175:
176: public String[] getFields() {
177: String[] values = new String[fields.size()];
178: StringBuffer buffer = new StringBuffer();
179: // Preserve the case of field names
180: int x = 0;
181: for (Iterator i = fields.values().iterator(); i.hasNext();) {
182: ValueHolder holder = (ValueHolder) i.next();
183: String key = holder.fieldName;
184: Object value = holder.fieldValue;
185: buffer.setLength(0);
186: buffer.append(key);
187: buffer.append("=");
188: if (value != null) {
189: if (value instanceof String) {
190: buffer.append(value.toString());
191: } else {
192: buffer.append("(");
193: buffer.append(value.toString());
194: buffer.append(")");
195: }
196: }
197: values[x++] = buffer.toString();
198: }
199: return values;
200: }
201:
202: public void setFields(String[] names, Object[] values)
203: throws RuntimeOperationsException {
204: if (names == null || values == null
205: || names.length != values.length) {
206: throw new RuntimeOperationsException(
207: new IllegalArgumentException("Invalid arguments"));
208: }
209:
210: for (int i = 0; i < names.length; ++i) {
211: setField(names[i], values[i]);
212: }
213: }
214:
215: public boolean isValid() throws RuntimeOperationsException {
216: if (getFieldValue("name") == null
217: || getFieldValue("descriptorType") == null)
218: return false;
219:
220: try {
221: for (Iterator i = fields.values().iterator(); i.hasNext();) {
222: ValueHolder holder = (ValueHolder) i.next();
223: checkField(holder.fieldName, holder.fieldValue);
224: }
225: return true;
226: } catch (RuntimeOperationsException x) {
227: return false;
228: }
229: }
230:
231: public String toXMLString() throws RuntimeOperationsException {
232: StringBuffer buf = new StringBuffer(32);
233: buf.append("<Descriptor>");
234:
235: try {
236: if (fields.size() != 0) {
237: for (Iterator i = fields.values().iterator(); i
238: .hasNext();) {
239: ValueHolder holder = (ValueHolder) i.next();
240: Object value = holder.fieldValue;
241: String valstr = toXMLValueString(value);
242: buf.append("<field name=\"");
243: buf.append(holder.fieldName);
244: buf.append("\" value=\"");
245: buf.append(valstr);
246: buf.append("\"></field>");
247: }
248: }
249: buf.append("</Descriptor>");
250: return buf.toString();
251: } catch (RuntimeException x) {
252: throw new RuntimeOperationsException(x);
253: }
254: }
255:
256: public String toString() throws RuntimeOperationsException {
257: StringBuffer buf = new StringBuffer();
258: try {
259: if (fields.size() != 0) {
260: for (Iterator i = fields.values().iterator(); i
261: .hasNext();) {
262: ValueHolder holder = (ValueHolder) i.next();
263: buf.append(holder.fieldName).append(" ").append(
264: holder.fieldValue);
265: if (i.hasNext()) {
266: buf.append(",");
267: }
268: }
269: }
270: return buf.toString();
271:
272: } catch (RuntimeOperationsException x) {
273: return buf.toString();
274: }
275: }
276:
277: private void addFieldFromXML(Node n) throws XMLParseException,
278: DOMException, RuntimeOperationsException {
279: if (!(n instanceof Element)) {
280: throw new XMLParseException("Invalid XML descriptor entity");
281: } else {
282: NamedNodeMap attributes = n.getAttributes();
283: if (attributes.getLength() != 2
284: && (attributes.getNamedItem("name") == null || attributes
285: .getNamedItem("value") == null)) {
286: throw new XMLParseException(
287: "Invalid XML descriptor element");
288: } else {
289: String name = attributes.getNamedItem("name")
290: .getNodeValue();
291: String value = attributes.getNamedItem("value")
292: .getNodeValue();
293: setField(name, parseValueString(value));
294: }
295: }
296: }
297:
298: private void checkField(String name, Object value)
299: throws RuntimeOperationsException {
300: if (name == null || name.trim().length() == 0) {
301: throw new RuntimeOperationsException(
302: new IllegalArgumentException("Illegal field name"));
303: }
304:
305: boolean isValid = true;
306:
307: // checks relaxed to match JavaDoc
308: if (name.equalsIgnoreCase("name")
309: || name.equalsIgnoreCase("descriptorType")) {
310: isValid = value instanceof String
311: && ((String) value).length() != 0;
312: } else if (name.equalsIgnoreCase("class")
313: || name.equalsIgnoreCase("role")
314: || name.equalsIgnoreCase("getMethod")
315: || name.equalsIgnoreCase("setMethod")) {
316: isValid = value instanceof String;
317: } else if (name.equalsIgnoreCase("persistPeriod")
318: || name.equalsIgnoreCase("currencyTimeLimit")
319: || name.equalsIgnoreCase("lastUpdatedTimeStamp")
320: || name.equalsIgnoreCase("lastReturnedTimeStamp")) {
321: if (value instanceof Number) {
322: isValid = ((Number) value).longValue() >= -1;
323: } else if (value instanceof String) {
324: try {
325: isValid = Long.parseLong((String) value) >= -1;
326: } catch (NumberFormatException e) {
327: isValid = false;
328: }
329: } else {
330: isValid = false;
331: }
332: } else if (name.equalsIgnoreCase("log")) {
333: if (value instanceof String) {
334: String s = (String) value;
335: isValid = "t".equalsIgnoreCase(s)
336: || "true".equalsIgnoreCase(s)
337: || "f".equalsIgnoreCase(s)
338: || "false".equalsIgnoreCase(s);
339: } else {
340: isValid = value instanceof Boolean;
341: }
342: } else if (name.equalsIgnoreCase("visibility")) {
343: if (value instanceof Number) {
344: long l = ((Number) value).longValue();
345: isValid = l >= 1 && l <= 4;
346: } else if (value instanceof String) {
347: try {
348: long l = Long.parseLong((String) value);
349: isValid = l >= 1 && l <= 4;
350: } catch (NumberFormatException e) {
351: isValid = false;
352: }
353: } else {
354: isValid = false;
355: }
356: } else if (name.equalsIgnoreCase("severity")) {
357: if (value instanceof Number) {
358: long l = ((Number) value).longValue();
359: isValid = l >= 0 && l <= 6;
360: } else if (value instanceof String) {
361: try {
362: long l = Long.parseLong((String) value);
363: isValid = l >= 0 && l <= 6;
364: } catch (NumberFormatException e) {
365: isValid = false;
366: }
367: } else {
368: isValid = false;
369: }
370: } else if (name.equalsIgnoreCase("persistPolicy")) {
371: if (value instanceof String) {
372: String s = (String) value;
373: isValid = "OnUpdate".equalsIgnoreCase(s)
374: || "OnTimer".equalsIgnoreCase(s)
375: || "NoMoreOftenThan".equalsIgnoreCase(s)
376: || "Always".equalsIgnoreCase(s)
377: || "Never".equalsIgnoreCase(s);
378: } else {
379: isValid = false;
380: }
381: }
382:
383: if (!isValid) {
384: throw new RuntimeOperationsException(
385: new IllegalArgumentException("Invalid value '"
386: + value + "' for field " + name));
387: }
388: }
389:
390: private Document documentFromXML(String xml)
391: throws XMLParseException {
392: try {
393: DocumentBuilder db = DocumentBuilderFactory.newInstance()
394: .newDocumentBuilder();
395: Document d = db.parse(new ByteArrayInputStream(xml
396: .getBytes()));
397: return d;
398: } catch (Exception x) {
399: throw new XMLParseException(x.toString());
400: }
401: }
402:
403: private Class getObjectValueClass(String value)
404: throws XMLParseException {
405: int eoc = value.indexOf("/");
406: if (eoc == -1) {
407: throw new XMLParseException(
408: "Illegal XML descriptor class name");
409: }
410: String klass = value.substring(1, eoc);
411: Class result = null;
412: try {
413: result = Thread.currentThread().getContextClassLoader()
414: .loadClass(klass);
415: } catch (Exception x) {
416: throw new XMLParseException(x.toString());
417: }
418: return result;
419: }
420:
421: private String getObjectValueString(String value)
422: throws XMLParseException {
423: int bov = value.indexOf("/");
424: if (bov == -1) {
425: throw new XMLParseException(
426: "Illegal XML descriptor object value");
427: }
428: return value.substring(bov + 1, value.length() - 1);
429: }
430:
431: private String objectClassToID(Class k) {
432: StringBuffer result = new StringBuffer();
433: result.append(k.getName());
434: result.append("/");
435: return result.toString();
436: }
437:
438: private Object parseValueString(String value)
439: throws XMLParseException {
440: Object result = null;
441: if (value.compareToIgnoreCase("(null)") == 0) {
442: result = null;
443: } else if (value.charAt(0) != '(') {
444: result = value;
445: } else {
446: result = parseObjectValueString(value);
447: }
448: return result;
449: }
450:
451: private Object parseObjectValueString(String value)
452: throws XMLParseException {
453: if (value.charAt(value.length() - 1) != ')') {
454: throw new XMLParseException("Invalid XML descriptor value");
455: }
456:
457: Object result = null;
458: Class k = getObjectValueClass(value);
459: String s = getObjectValueString(value);
460: try {
461: if (k != Character.class) {
462: result = k.getConstructor(new Class[] { String.class })
463: .newInstance(new Object[] { s });
464: } else {
465: result = new Character(s.charAt(0));
466: }
467: } catch (Exception x) {
468: throw new XMLParseException(x.toString());
469: }
470: return result;
471: }
472:
473: private String toXMLValueString(Object value) {
474: String result;
475: if (value == null) {
476: result = "(null)";
477: } else {
478: Class k = value.getClass();
479: if (k == String.class && ((String) value).charAt(0) != '(') {
480: result = (String) value;
481: } else {
482: result = toObjectXMLValueString(k, value);
483: }
484: }
485: return result;
486: }
487:
488: private String toObjectXMLValueString(Class k, Object value) {
489: StringBuffer result = new StringBuffer();
490: result.append("(");
491: result.append(objectClassToID(k));
492: result.append(value.toString());
493: result.append(")");
494: return result.toString();
495: }
496:
497: private void readObject(ObjectInputStream stream)
498: throws IOException, ClassNotFoundException {
499: HashMap desc = (HashMap) stream.readFields().get("descriptor",
500: null);
501: fields = new HashMap(desc.size());
502: for (Iterator i = desc.entrySet().iterator(); i.hasNext();) {
503: Map.Entry entry = (Map.Entry) i.next();
504: String name = (String) entry.getKey();
505: fields.put(name.toLowerCase(), new ValueHolder(name, entry
506: .getValue()));
507: }
508: }
509:
510: private void writeObject(ObjectOutputStream stream)
511: throws IOException {
512: HashMap desc = new HashMap(fields.size());
513: for (Iterator i = fields.values().iterator(); i.hasNext();) {
514: ValueHolder holder = (ValueHolder) i.next();
515: desc.put(holder.fieldName, holder.fieldValue);
516: }
517: ObjectOutputStream.PutField fields = stream.putFields();
518: fields.put("descriptor", desc);
519: stream.writeFields();
520: }
521:
522: private static class ValueHolder {
523: private final String fieldName;
524: private final Object fieldValue;
525:
526: private ValueHolder(String fieldName, Object value) {
527: this.fieldName = fieldName;
528: this.fieldValue = value;
529: }
530: }
531: }
|