001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package javax.management.modelmbean;
023:
024: import java.io.ByteArrayOutputStream;
025: import java.io.IOException;
026: import java.io.ObjectInputStream;
027: import java.io.ObjectOutputStream;
028: import java.io.ObjectStreamField;
029: import java.io.Serializable;
030: import java.io.StreamCorruptedException;
031: import java.io.StringReader;
032: import java.io.StringWriter;
033: import java.util.Arrays;
034: import java.util.Collections;
035: import java.util.HashMap;
036: import java.util.Iterator;
037: import java.util.Map;
038:
039: import javax.management.Descriptor;
040: import javax.management.MBeanException;
041: import javax.management.RuntimeOperationsException;
042:
043: import org.jboss.dom4j.Attribute;
044: import org.jboss.dom4j.Document;
045: import org.jboss.dom4j.DocumentException;
046: import org.jboss.dom4j.DocumentHelper;
047: import org.jboss.dom4j.Element;
048: import org.jboss.dom4j.io.OutputFormat;
049: import org.jboss.dom4j.io.SAXReader;
050: import org.jboss.dom4j.io.XMLWriter;
051: import org.jboss.mx.modelmbean.ModelMBeanConstants;
052: import org.jboss.mx.util.Serialization;
053: import org.jboss.util.xml.JBossEntityResolver;
054:
055: /**
056: * Support class for creating descriptors.
057: *
058: * @see javax.management.Descriptor
059: *
060: * @author <a href="mailto:juha@jboss.org">Juha Lindfors</a>.
061: * @author <a href="mailto:adrian.brock@happeningtimes.com">Adrian Brock</a>.
062: * @author <a href="mailto:thomas.diesler@jboss.org">Thomas Diesler</a>.
063: * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a> *
064: * @version $Revision: 57200 $
065: */
066: public class DescriptorSupport implements Descriptor {
067:
068: // TODO: the spec doesn't define equality for descriptors
069: // we should override equals to match descriptor field, value pairs
070: // this does not appear to be the case with the 1.0 RI though
071:
072: // Attributes ----------------------------------------------------
073:
074: /**
075: * Map for the descriptor field -> value.
076: */
077: private Map fieldMap;
078:
079: // Static --------------------------------------------------------
080:
081: private static final int DEFAULT_SIZE = 20;
082:
083: private static final long serialVersionUID;
084: private static final ObjectStreamField[] serialPersistentFields;
085:
086: static {
087: switch (Serialization.version) {
088: case Serialization.V1R0:
089: serialVersionUID = 8071560848919417985L;
090: break;
091: default:
092: serialVersionUID = -6292969195866300415L;
093: }
094: serialPersistentFields = new ObjectStreamField[] { new ObjectStreamField(
095: "descriptor", HashMap.class) };
096: }
097:
098: // Constructors --------------------------------------------------
099: /**
100: * Default constructor.
101: */
102: public DescriptorSupport() {
103: fieldMap = Collections
104: .synchronizedMap(new HashMap(DEFAULT_SIZE));
105: }
106:
107: /**
108: * Creates descriptor instance with a given initial size.
109: *
110: * @param initialSize initial size of the descriptor
111: * @throws MBeanException this exception is never thrown but is declared here
112: * for Sun RI API compatibility
113: * @throws RuntimeOperationsException if the <tt>initialSize</tt> is zero or negative. The target
114: * exception wrapped by this exception is an instace of <tt>IllegalArgumentException</tt> class.
115: */
116: public DescriptorSupport(int initialSize) throws MBeanException {
117: if (initialSize <= 0)
118: // required by RI javadoc
119: throw new RuntimeOperationsException(
120: new IllegalArgumentException("initialSize <= 0"));
121:
122: fieldMap = Collections
123: .synchronizedMap(new HashMap(initialSize));
124: }
125:
126: /**
127: * Copy constructor.
128: *
129: * @param descriptor the descriptor to be copied
130: * @throws RuntimeOperationsException if descriptor is null. The target exception wrapped by this
131: * exception is an instance of <tt>IllegalArgumentException</tt> class.
132: */
133: public DescriptorSupport(DescriptorSupport descriptor) {
134: if (descriptor != null) {
135: String[] fieldNames = descriptor.getFieldNames();
136: fieldMap = Collections.synchronizedMap(new HashMap(
137: fieldNames.length));
138: this .setFields(fieldNames, descriptor
139: .getFieldValues(fieldNames));
140: } else {
141: fieldMap = Collections.synchronizedMap(new HashMap(
142: DEFAULT_SIZE));
143: }
144: }
145:
146: /**
147: * Creates descriptor instance with given field names and values.if both field names and field
148: * values array contain empty arrays, an empty descriptor is created.
149: * None of the name entries in the field names array can be a <tt>null</tt> reference.
150: * Field values may contain <tt>null</tt> references.
151: *
152: * @param fieldNames Contains names for the descriptor fields. This array cannot contain
153: * <tt>null</tt> references. If both <tt>fieldNames</tt> and <tt>fieldValues</tt>
154: * arguments contain <tt>null</tt> or empty array references then an empty descriptor
155: * is created. The size of the <tt>fieldNames</tt> array must match the size of
156: * the <tt>fieldValues</tt> array.
157: * @param fieldValues Contains values for the descriptor fields. Null references are allowed.
158: *
159: * @throws RuntimeOperationsException if array sizes don't match
160: */
161: public DescriptorSupport(String[] fieldNames, Object[] fieldValues)
162: throws RuntimeOperationsException {
163: fieldMap = Collections
164: .synchronizedMap(new HashMap(DEFAULT_SIZE));
165: setFields(fieldNames, fieldValues);
166: }
167:
168: public DescriptorSupport(String[] fields) {
169: if (fields == null) {
170: fieldMap = Collections.synchronizedMap(new HashMap(
171: DEFAULT_SIZE));
172: return;
173: }
174:
175: int j = 0;
176: for (int i = 0; i < fields.length; ++i) {
177: if (fields[i] != null && fields[i].length() != 0) {
178: ++j;
179: }
180: }
181:
182: fieldMap = Collections.synchronizedMap(new HashMap(j));
183: String[] names = new String[j];
184: String[] values = new String[j];
185:
186: j = 0;
187: for (int i = 0; i < fields.length; ++i) {
188: if (fields[i] == null || fields[i].length() == 0)
189: continue;
190:
191: try {
192: int index = fields[i].indexOf('=');
193: if (index == -1)
194: throw new IllegalArgumentException("Invalid field "
195: + fields[i]);
196:
197: names[j] = fields[i].substring(0, index);
198: if (index == fields[i].length() - 1)
199: values[j] = null;
200: else
201: values[j] = fields[i].substring(index + 1,
202: fields[i].length());
203: ++j;
204: } catch (RuntimeException e) {
205: throw new RuntimeOperationsException(e,
206: "Error in field " + i);
207: }
208: }
209:
210: setFields(names, values);
211: }
212:
213: /**
214: * Descriptor constructor taking an XML String.
215: * In this implementation, all field values will be created as Strings.
216: * If the field values are not Strings, the programmer will have to reset or convert these fields correctly.
217: */
218: public DescriptorSupport(String xmlString) throws MBeanException,
219: RuntimeOperationsException, XMLParseException {
220: if (xmlString == null)
221: throw new RuntimeOperationsException(
222: new IllegalArgumentException("Null xmlString"));
223:
224: fieldMap = Collections
225: .synchronizedMap(new HashMap(DEFAULT_SIZE));
226: try {
227: SAXReader saxReader = new SAXReader();
228: saxReader.setEntityResolver(new JBossEntityResolver());
229:
230: Document document = saxReader.read(new StringReader(
231: xmlString));
232: Element root = document.getRootElement();
233: String rootName = root.getName();
234: if (rootName.equalsIgnoreCase("Descriptor")) {
235: // iterate through child elements of root
236: for (Iterator i = root.elementIterator(); i.hasNext();) {
237: Element element = (Element) i.next();
238: if (element.getName().equals("field")) {
239: Attribute attr = element.attribute("name");
240: if (attr != null) {
241: String name = attr.getText();
242: String value = element.getTextTrim();
243: setField(name, value);
244: } else {
245: throw new XMLParseException(
246: "Cannot find attribute 'name' in "
247: + element);
248: }
249: }
250: }
251: } else {
252: RuntimeException ex = new IllegalArgumentException(
253: "Root element must be Descriptor, saw: "
254: + rootName);
255: throw new RuntimeOperationsException(ex);
256: }
257: } catch (DocumentException e) {
258: throw new XMLParseException(e, "Cannot parse XML string: "
259: + xmlString);
260: }
261: }
262:
263: // Public --------------------------------------------------------
264: public Object getFieldValue(String inFieldName) {
265: try {
266: checkFieldName(inFieldName);
267: return fieldMap.get(new FieldName(inFieldName));
268: } catch (RuntimeException e) {
269: throw new RuntimeOperationsException(e, e.toString());
270: }
271: }
272:
273: public void setField(String inFieldName, Object fieldValue) {
274: try {
275: checkFieldName(inFieldName);
276: validateField(inFieldName, fieldValue);
277: fieldMap.put(new FieldName(inFieldName), fieldValue);
278: } catch (RuntimeException e) {
279: throw new RuntimeOperationsException(e, e.toString());
280: }
281: }
282:
283: /**
284: * Returns String array of fields in the format fieldName=fieldValue.
285: * If there are no fields in the descriptor, then an empty String array is returned.
286: * If a fieldValue is not a String then the toString() method is called on it and its returned value is used as
287: * the value for the field enclosed in parenthesis.
288: */
289: public String[] getFields() {
290: String[] fieldStrings = new String[fieldMap.size()];
291: Iterator it = fieldMap.keySet().iterator();
292: synchronized (fieldMap) {
293: for (int i = 0; i < fieldMap.size(); ++i) {
294: FieldName key = (FieldName) it.next();
295: Object value = fieldMap.get(key);
296: if (value != null) {
297: if (value instanceof String)
298: fieldStrings[i] = key + "=" + value;
299: else
300: fieldStrings[i] = key + "=(" + value + ")";
301: } else {
302: fieldStrings[i] = key + "=";
303: }
304: }
305: }
306:
307: return fieldStrings;
308: }
309:
310: /**
311: * Returns string array of fields names. If the descriptor is empty, you will get an empty array.
312: */
313: public String[] getFieldNames() {
314: String[] fields = new String[fieldMap.size()];
315: Iterator it = fieldMap.keySet().iterator();
316: synchronized (fieldMap) {
317: for (int i = 0; i < fieldMap.size(); ++i) {
318: FieldName key = (FieldName) it.next();
319: fields[i] = key.getName();
320: }
321: }
322: return fields;
323: }
324:
325: /**
326: * Returns all the field values in the descriptor as an array of Objects.
327: * The returned values are in the same order as the fieldNames String array parameter.
328: */
329: public Object[] getFieldValues(String[] fieldNames) {
330: if (fieldMap.size() == 0)
331: return new Object[0];
332:
333: Object[] values = null;
334: if (fieldNames == null) {
335: values = new Object[fieldMap.size()];
336: Iterator it = fieldMap.values().iterator();
337: synchronized (fieldMap) {
338: for (int i = 0; i < fieldMap.size(); ++i)
339: values[i] = it.next();
340: }
341: } else {
342: values = new Object[fieldNames.length];
343: for (int i = 0; i < fieldNames.length; ++i) {
344: if (fieldNames[i] == null || fieldNames[i].equals(""))
345: values[i] = null;
346: else
347: values[i] = fieldMap.get(new FieldName(
348: fieldNames[i]));
349: }
350: }
351:
352: return values;
353: }
354:
355: /**
356: * Sets all Fields in the list to the new value in with the same index in the fieldValue array.
357: * Array sizes must match. The field value will be validated before it is set (by calling the method isValid)
358: * If it is not valid, then an exception will be thrown. If the arrays are empty, then no change will take effect.
359: */
360: public void setFields(String[] fieldNames, Object[] fieldValues) {
361: if (fieldNames == null || fieldValues == null)
362: throw new RuntimeOperationsException(
363: new IllegalArgumentException(
364: "fieldNames or fieldValues was null."));
365:
366: if (fieldNames.length == 0 && fieldValues.length == 0)
367: return;
368:
369: if (fieldNames.length != fieldValues.length)
370: throw new RuntimeOperationsException(
371: new IllegalArgumentException(
372: "fieldNames and fieldValues array size must match."));
373:
374: try {
375: for (int i = 0; i < fieldNames.length; ++i) {
376: String name = fieldNames[i];
377: checkFieldName(name);
378: validateField(name, fieldValues[i]);
379: fieldMap.put(new FieldName(name), fieldValues[i]);
380: }
381: } catch (IllegalArgumentException e) {
382: throw new RuntimeOperationsException(e);
383: }
384: }
385:
386: public synchronized Object clone() {
387: try {
388: DescriptorSupport clone = (DescriptorSupport) super .clone();
389:
390: clone.fieldMap = Collections.synchronizedMap(new HashMap(
391: this .fieldMap));
392:
393: return clone;
394: } catch (CloneNotSupportedException e) {
395: // Descriptor interface won't allow me to throw CNSE
396: throw new RuntimeOperationsException(new RuntimeException(e
397: .getMessage()), e.toString());
398: }
399: }
400:
401: public void removeField(String fieldName) {
402: if (fieldName == null || fieldName.equals(""))
403: return;
404:
405: fieldMap.remove(new FieldName(fieldName));
406: }
407:
408: /**
409: * Returns true if all of the fields have legal values given their names.
410: *
411: * This implementation does not support interopreating with a directory or lookup service.
412: * Thus, conforming to the specification, no checking is done on the "export" field.
413: *
414: * Otherwise this implementation returns false if:
415: * - name and descriptorType fieldNames are not defined, or null, or empty, or not String
416: * - class, role, getMethod, setMethod fieldNames, if defined, are null or not String
417: * - persistPeriod, currencyTimeLimit, lastUpdatedTimeStamp, lastReturnedTimeStamp if defined, are null, or not a Numeric String or not a Numeric Value >= -1
418: * - log fieldName, if defined, is null, or not a Boolean or not a String with value "t", "f", "true", "false". These String values must not be case sensitive.
419: * - visibility fieldName, if defined, is null, or not a Numeric String or a not Numeric Value >= 1 and <= 4
420: * - severity fieldName, if defined, is null, or not a Numeric String or not a Numeric Value >= 1 and <= 5
421: * - persistPolicy fieldName, if defined, is null, or not a following String :
422: * - "OnUpdate", "OnTimer", "NoMoreOftenThan", "Always", "Never". These String values must not be case sensitive.
423: *
424: * @return true if the values are legal.
425: * @throws RuntimeOperationsException If the validity checking fails for any reason, this exception will be thrown.
426:
427: */
428: public boolean isValid() throws RuntimeOperationsException {
429: try {
430: validateString(ModelMBeanConstants.NAME, true);
431: validateString(ModelMBeanConstants.DESCRIPTOR_TYPE, true);
432:
433: synchronized (fieldMap) {
434: for (Iterator i = fieldMap.entrySet().iterator(); i
435: .hasNext();) {
436: Map.Entry entry = (Map.Entry) i.next();
437: FieldName name = (FieldName) entry.getKey();
438: Object value = entry.getValue();
439: validateField(name.getName(), value);
440: }
441: }
442: } catch (RuntimeException e) {
443: return false;
444: }
445:
446: return true;
447: }
448:
449: /**
450: * Returns an XML String representing the descriptor.
451: */
452: public String toXMLString() throws RuntimeOperationsException {
453: // Return the javadoc specified empty representation
454: if (fieldMap.size() == 0)
455: return "<Descriptor></Descriptor>";
456:
457: /* Build the non-empty rep
458: <Descriptor name='...' field='...' />
459: */
460: try {
461: Document document = DocumentHelper.createDocument();
462: Element root = document.addElement("Descriptor");
463: String[] names = getFieldNames();
464: for (int i = 0; i < names.length; i++) {
465: String name = names[i];
466: Object value = getFieldValue(name);
467: Element field = root.addElement("field");
468: field.addAttribute("name", name);
469: field.addText(value.toString());
470: }
471:
472: StringWriter sw = new StringWriter();
473: OutputFormat format = OutputFormat.createPrettyPrint();
474: XMLWriter writer = new XMLWriter(sw, format);
475: writer.write(document);
476: writer.close();
477: return sw.toString();
478: } catch (IOException e) {
479: throw new RuntimeOperationsException(
480: new RuntimeException(e),
481: "Cannot get XML representation");
482: }
483: }
484:
485: // Object overrides ----------------------------------------------
486:
487: public String toString() {
488: String[] names = getFieldNames();
489: Object[] values = getFieldValues(names);
490:
491: StringBuffer sbuf = new StringBuffer(500);
492: sbuf.append(getClass()).append('@').append(
493: System.identityHashCode(this )).append('[');
494:
495: if (names.length == 0)
496: return "<empty descriptor>";
497: else {
498: for (int i = 0; i < values.length; ++i) {
499: sbuf.append(names[i]);
500: sbuf.append("=");
501: sbuf.append(values[i]);
502: if (i < values.length - 1)
503: sbuf.append(",");
504: }
505: }
506:
507: sbuf.append(']');
508:
509: return sbuf.toString();
510: }
511:
512: // Private -----------------------------------------------------
513:
514: private void checkFieldName(String inFieldName) {
515: if (inFieldName == null || inFieldName.equals(""))
516: throw new IllegalArgumentException(
517: "null or empty field name");
518: }
519:
520: private void validateField(String inFieldName, Object value) {
521: String fieldName = inFieldName;
522: if (fieldName.equalsIgnoreCase(ModelMBeanConstants.NAME))
523: validateString(inFieldName, value, true);
524: else if (fieldName
525: .equalsIgnoreCase(ModelMBeanConstants.DESCRIPTOR_TYPE))
526: validateString(inFieldName, value, true);
527: else if (fieldName.equalsIgnoreCase(ModelMBeanConstants.CLASS))
528: validateString(inFieldName, value, false);
529: else if (fieldName.equalsIgnoreCase(ModelMBeanConstants.ROLE))
530: validateString(inFieldName, value, false);
531: else if (fieldName
532: .equalsIgnoreCase(ModelMBeanConstants.GET_METHOD))
533: validateString(inFieldName, value, false);
534: else if (fieldName
535: .equalsIgnoreCase(ModelMBeanConstants.SET_METHOD))
536: validateString(inFieldName, value, false);
537: else if (fieldName
538: .equalsIgnoreCase(ModelMBeanConstants.PERSIST_PERIOD))
539: validateNumeric(inFieldName, value);
540: else if (fieldName
541: .equalsIgnoreCase(ModelMBeanConstants.CURRENCY_TIME_LIMIT))
542: validateNumeric(inFieldName, value);
543: else if (fieldName
544: .equalsIgnoreCase(ModelMBeanConstants.LAST_UPDATED_TIME_STAMP))
545: validateNumeric(inFieldName, value);
546: else if (fieldName
547: .equalsIgnoreCase(ModelMBeanConstants.LAST_UPDATED_TIME_STAMP2))
548: validateNumeric(inFieldName, value);
549: else if (fieldName
550: .equalsIgnoreCase(ModelMBeanConstants.LAST_RETURNED_TIME_STAMP))
551: validateNumeric(inFieldName, value);
552: else if (fieldName.equalsIgnoreCase(ModelMBeanConstants.LOG))
553: validateBoolean(inFieldName, value);
554: else if (fieldName
555: .equalsIgnoreCase(ModelMBeanConstants.VISIBILITY))
556: validateNumeric(inFieldName, value, 1, 4);
557: else if (fieldName
558: .equalsIgnoreCase(ModelMBeanConstants.SEVERITY))
559: validateNumeric(inFieldName, value, 1, 6);
560: else if (fieldName
561: .equalsIgnoreCase(ModelMBeanConstants.PERSIST_POLICY))
562: validatePersistPolicy(inFieldName, value);
563: }
564:
565: private void validateString(String fieldName, boolean mandatory) {
566: Object value = fieldMap.get(new FieldName(fieldName));
567: validateString(fieldName, value, mandatory);
568: }
569:
570: private void validateString(String fieldName, Object value,
571: boolean mandatory) {
572: if (value == null && mandatory == true)
573: throw new IllegalArgumentException(
574: "Expected a value for mandatory field '"
575: + fieldName + "'");
576: else if (value == null)
577: throw new IllegalArgumentException(
578: "Expected a value for field '" + fieldName + "'");
579: if ((value instanceof String) == false)
580: throw new IllegalArgumentException(
581: "Expected a String for field '" + fieldName + "'");
582: String string = (String) value;
583: if (string.length() == 0)
584: throw new IllegalArgumentException(
585: "Empty value for field '" + fieldName + "'");
586: }
587:
588: private void validatePersistPolicy(String fieldName, Object value) {
589: validateString(fieldName, value, false);
590: String string = ((String) value);
591: String[] policies = ModelMBeanConstants.PERSIST_POLICIES;
592: for (int i = 0; i < policies.length; ++i) {
593: if (policies[i].equalsIgnoreCase(string))
594: return;
595: }
596: throw new IllegalArgumentException("Invalid value " + value
597: + " for field '" + fieldName + "' expected one of "
598: + Arrays.asList(policies));
599: }
600:
601: private void validateBoolean(String fieldName, Object value) {
602: if (value == null)
603: throw new IllegalArgumentException(
604: "Expected a value for field '" + fieldName + "'");
605: if (value instanceof String) {
606: String string = ((String) value);
607: if (string.equalsIgnoreCase("T")
608: || string.equalsIgnoreCase("F"))
609: return;
610: if (string.equalsIgnoreCase("TRUE")
611: || string.equalsIgnoreCase("FALSE"))
612: return;
613: } else if (value instanceof Boolean)
614: return;
615: throw new IllegalArgumentException("Invalid value " + value
616: + " for field '" + fieldName + "'");
617: }
618:
619: private long validateNumeric(String fieldName, Object value) {
620: if (value == null)
621: throw new IllegalArgumentException(
622: "Expected a value for field '" + fieldName + "'");
623:
624: Long number = null;
625: if (value instanceof String)
626: number = new Long((String) value);
627: else if (value instanceof Number)
628: number = new Long(((Number) value).longValue());
629: if (number != null && number.longValue() >= -1)
630: return number.longValue();
631:
632: throw new IllegalArgumentException("Invalid value " + value
633: + " for field '" + fieldName + "'");
634: }
635:
636: private void validateNumeric(String fieldName, Object value,
637: int min, int max) {
638: long result = validateNumeric(fieldName, value);
639: if (result >= min && result <= max)
640: return;
641: throw new IllegalArgumentException("Invalid value " + value
642: + " for field '" + fieldName + "'");
643: }
644:
645: private void readObject(ObjectInputStream ois) throws IOException,
646: ClassNotFoundException {
647: ObjectInputStream.GetField getField = ois.readFields();
648: HashMap serMap = (HashMap) getField.get("descriptor", null);
649: if (serMap == null)
650: throw new StreamCorruptedException("Null descriptor?");
651:
652: // replace the keys with FieldName objects
653: fieldMap = Collections.synchronizedMap(new HashMap());
654: Iterator it = serMap.entrySet().iterator();
655: while (it.hasNext()) {
656: Map.Entry entry = (Map.Entry) it.next();
657: FieldName key = new FieldName((String) entry.getKey());
658: fieldMap.put(key, entry.getValue());
659: }
660: }
661:
662: private void writeObject(ObjectOutputStream oos) throws IOException {
663: ObjectOutputStream.PutField putField = oos.putFields();
664: /* Since non-Serializable values can be put into the descriptor
665: just remove them when writing out the serialized form
666: */
667: ByteArrayOutputStream baos = new ByteArrayOutputStream();
668: ObjectOutputStream tstOOS = new ObjectOutputStream(baos);
669:
670: // replace the keys with strings
671: HashMap serMap = new HashMap();
672: Iterator it = fieldMap.entrySet().iterator();
673: while (it.hasNext()) {
674: Map.Entry entry = (Map.Entry) it.next();
675: String key = ((FieldName) entry.getKey()).name;
676: Object value = entry.getValue();
677: if (value instanceof Serializable) {
678: // Validate that the object's references are serializable
679: try {
680: baos.reset();
681: tstOOS.writeObject(value);
682: serMap.put(key, value);
683: } catch (Exception ignore) {
684: }
685: }
686: }
687: baos.close();
688: tstOOS.close();
689:
690: putField.put("descriptor", serMap);
691: oos.writeFields();
692: }
693:
694: /**
695: * Provides case insensitive hashCode, equals.
696: */
697: private class FieldName implements Serializable {
698: static final long serialVersionUID = 2645619836053638810L;
699: private String name;
700: private int hashCode;
701:
702: public FieldName(String aName) {
703: if (aName == null)
704: throw new IllegalArgumentException("null name");
705: this .name = aName;
706: }
707:
708: public String getName() {
709: return name;
710: }
711:
712: public int hashCode() {
713: if (hashCode == 0)
714: return hashCode = name.toLowerCase().hashCode();
715: else
716: return hashCode;
717: }
718:
719: public boolean equals(Object obj) {
720: if (obj == null)
721: return false;
722: if (obj == this )
723: return true;
724: if (obj instanceof FieldName)
725: return name.equalsIgnoreCase(((FieldName) obj).name);
726: if (obj instanceof String)
727: return name.equalsIgnoreCase((String) obj);
728: return false;
729: }
730:
731: public String toString() {
732: return name;
733: }
734:
735: }
736: }
|