KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > management > modelmbean > DescriptorSupport


1 /*
2  * @(#)file DescriptorSupport.java
3  * @(#)author IBM Corp.
4  * @(#)version 1.54
5  * @(#)lastedit 07/07/24
6  */

7 /*
8  * Copyright IBM Corp. 1999-2000. All rights reserved.
9  *
10  * The program is provided "as is" without any warranty express or implied,
11  * including the warranty of non-infringement and the implied warranties of
12  * merchantibility and fitness for a particular purpose. IBM will not be
13  * liable for any damages suffered by you or any third party claim against
14  * you regarding the Program.
15  *
16  * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
17  * This software is the proprietary information of Sun Microsystems, Inc.
18  * Use is subject to license terms.
19  *
20  * Copyright 2005 Sun Microsystems, Inc. Tous droits reserves.
21  * Ce logiciel est propriete de Sun Microsystems, Inc.
22  * Distribue par des licences qui en restreignent l'utilisation.
23  *
24  */

25
26 package javax.management.modelmbean;
27
28 import java.io.IOException JavaDoc;
29 import java.io.ObjectInputStream JavaDoc;
30 import java.io.ObjectOutputStream JavaDoc;
31 import java.io.ObjectStreamField JavaDoc;
32
33 import java.lang.reflect.Constructor JavaDoc;
34
35 import java.security.AccessController JavaDoc;
36 import java.util.HashMap JavaDoc;
37 import java.util.Iterator JavaDoc;
38 import java.util.Map JavaDoc;
39 import java.util.Set JavaDoc;
40 import java.util.StringTokenizer JavaDoc;
41
42 import javax.management.RuntimeOperationsException JavaDoc;
43 import javax.management.MBeanException JavaDoc;
44
45 import com.sun.jmx.mbeanserver.GetPropertyAction;
46
47 import com.sun.jmx.trace.Trace;
48 import java.util.Collections JavaDoc;
49 import java.util.SortedMap JavaDoc;
50 import java.util.TreeMap JavaDoc;
51
52 import sun.reflect.misc.ReflectUtil;
53
54 /**
55  * This class represents the metadata set for a ModelMBean element. A
56  * descriptor is part of the ModelMBeanInfo,
57  * ModelMBeanNotificationInfo, ModelMBeanAttributeInfo,
58  * ModelMBeanConstructorInfo, and ModelMBeanParameterInfo.
59  * <P>
60  * A descriptor consists of a collection of fields. Each field is in
61  * fieldname=fieldvalue format. Field names are not case sensitive,
62  * case will be preserved on field values.
63  * <P>
64  * All field names and values are not predefined. New fields can be
65  * defined and added by any program. Some fields have been predefined
66  * for consistency of implementation and support by the
67  * ModelMBeanInfo, ModelMBeanAttributeInfo, ModelMBeanConstructorInfo,
68  * ModelMBeanNotificationInfo, ModelMBeanOperationInfo and ModelMBean
69  * classes.
70  *
71  * @since 1.5
72  */

73
74 public class DescriptorSupport
75      implements javax.management.Descriptor JavaDoc
76 {
77
78     // Serialization compatibility stuff:
79
// Two serial forms are supported in this class. The selected form depends
80
// on system property "jmx.serial.form":
81
// - "1.0" for JMX 1.0
82
// - any other value for JMX 1.1 and higher
83
//
84
// Serial version for old serial form
85
private static final long oldSerialVersionUID = 8071560848919417985L;
86     //
87
// Serial version for new serial form
88
private static final long newSerialVersionUID = -6292969195866300415L;
89     //
90
// Serializable fields in old serial form
91
private static final ObjectStreamField JavaDoc[] oldSerialPersistentFields =
92     {
93       new ObjectStreamField JavaDoc("descriptor", HashMap JavaDoc.class),
94       new ObjectStreamField JavaDoc("currClass", String JavaDoc.class)
95     };
96     //
97
// Serializable fields in new serial form
98
private static final ObjectStreamField JavaDoc[] newSerialPersistentFields =
99     {
100       new ObjectStreamField JavaDoc("descriptor", HashMap JavaDoc.class)
101     };
102     //
103
// Actual serial version and serial form
104
private static final long serialVersionUID;
105     /**
106      * @serialField descriptor HashMap The collection of fields representing this descriptor
107      */

108     private static final ObjectStreamField JavaDoc[] serialPersistentFields;
109     private static final String JavaDoc serialForm;
110     static {
111     String JavaDoc form = null;
112     boolean compat = false;
113     try {
114         GetPropertyAction act = new GetPropertyAction("jmx.serial.form");
115         form = (String JavaDoc) AccessController.doPrivileged(act);
116         compat = "1.0".equals(form); // form may be null
117
} catch (Exception JavaDoc e) {
118         // OK: No compat with 1.0
119
}
120     serialForm = form;
121     if (compat) {
122         serialPersistentFields = oldSerialPersistentFields;
123         serialVersionUID = oldSerialVersionUID;
124     } else {
125         serialPersistentFields = newSerialPersistentFields;
126         serialVersionUID = newSerialVersionUID;
127     }
128     }
129     //
130
// END Serialization compatibility stuff
131

132     /* Spec says that field names are case-insensitive, but that case
133        is preserved. This means that we need to be able to map from a
134        name that may differ in case to the actual name that is used in
135        the HashMap. Thus, descriptorMap is a TreeMap with a Comparator
136        that ignores case.
137
138        Previous versions of this class had a field called "descriptor"
139        of type HashMap where the keys were directly Strings. This is
140        hard to reconcile with the required semantics, so we fabricate
141        that field virtually during serialization and deserialization
142        but keep the real information in descriptorMap.
143     */

144     private transient SortedMap JavaDoc<String JavaDoc, Object JavaDoc> descriptorMap;
145
146     private static final int DEFAULT_SIZE = 20;
147     private static final String JavaDoc currClass = "DescriptorSupport";
148
149
150     /**
151      * Descriptor default constructor.
152      * Default initial descriptor size is 20. It will grow as needed.<br>
153      * Note that the created empty descriptor is not a valid descriptor
154      * (the method {@link #isValid isValid} returns <CODE>false</CODE>)
155      */

156     public DescriptorSupport() {
157         if (tracing())
158             trace("DescriptorSupport()", "Constructor");
159         init(null);
160     }
161
162     /**
163      * Descriptor constructor. Takes as parameter the initial
164      * capacity of the Map that stores the descriptor fields.
165      * Capacity will grow as needed.<br> Note that the created empty
166      * descriptor is not a valid descriptor (the method {@link
167      * #isValid isValid} returns <CODE>false</CODE>).
168      *
169      * @param initNumFields The initial capacity of the Map that
170      * stores the descriptor fields.
171      *
172      * @exception RuntimeOperationsException for illegal value for
173      * initNumFields (&lt;= 0)
174      * @exception MBeanException Wraps a distributed communication Exception.
175      */

176     public DescriptorSupport(int initNumFields)
177         throws MBeanException JavaDoc, RuntimeOperationsException JavaDoc {
178         if (tracing()) {
179             trace("Descriptor(initNumFields=" + initNumFields + ")",
180                   "Constructor");
181         }
182     if (initNumFields <= 0) {
183         if (tracing()) {
184         trace("Descriptor(maxNumFields)",
185               "Illegal arguments: initNumFields <= 0");
186         }
187         final String JavaDoc msg =
188         "Descriptor field limit invalid: " + initNumFields;
189         final RuntimeException JavaDoc iae = new IllegalArgumentException JavaDoc(msg);
190         throw new RuntimeOperationsException JavaDoc(iae, msg);
191     }
192         init(null);
193     }
194
195     /**
196      * Descriptor constructor taking a Descriptor as parameter.
197      * Creates a new descriptor initialized to the values of the
198      * descriptor passed in parameter.
199      *
200      * @param inDescr the descriptor to be used to initialize the
201      * constructed descriptor. If it is null or contains no descriptor
202      * fields, an empty Descriptor will be created.
203      */

204     public DescriptorSupport(DescriptorSupport JavaDoc inDescr) {
205     if (tracing()) {
206         trace("Descriptor(Descriptor)","Constructor");
207     }
208     if (inDescr == null)
209             init(null);
210         else
211             init(inDescr.descriptorMap);
212     }
213
214
215     /**
216      * <p>Descriptor constructor taking an XML String.</p>
217      *
218      * <p>The format of the XML string is not defined, but an
219      * implementation must ensure that the string returned by
220      * {@link #toXMLString() toXMLString()} on an existing
221      * descriptor can be used to instantiate an equivalent
222      * descriptor using this constructor.</p>
223      *
224      * <p>In this implementation, all field values will be created
225      * as Strings. If the field values are not Strings, the
226      * programmer will have to reset or convert these fields
227      * correctly.</p>
228      *
229      * @param inStr An XML-formatted string used to populate this
230      * Descriptor. The format is not defined, but any
231      * implementation must ensure that the string returned by
232      * method {@link #toXMLString toXMLString} on an existing
233      * descriptor can be used to instantiate an equivalent
234      * descriptor when instantiated using this constructor.
235      *
236      * @exception RuntimeOperationsException If the String inStr
237      * passed in parameter is null
238      * @exception XMLParseException XML parsing problem while parsing
239      * the input String
240      * @exception MBeanException Wraps a distributed communication Exception.
241      */

242     /* At some stage we should rewrite this code to be cleverer. Using
243        a StringTokenizer as we do means, first, that we accept a lot of
244        bogus strings without noticing they are bogus, and second, that we
245        split the string being parsed at characters like > even if they
246        occur in the middle of a field value. */

247     public DescriptorSupport(String JavaDoc inStr)
248         throws MBeanException JavaDoc, RuntimeOperationsException JavaDoc,
249            XMLParseException JavaDoc {
250     /* parse an XML-formatted string and populate internal
251      * structure with it */

252     if (tracing()) {
253         trace("Descriptor(String ='" + inStr + "')","Constructor");
254     }
255     if (inStr == null) {
256         if (tracing()) {
257         trace("Descriptor(String = null)","Illegal arguments");
258         }
259         final String JavaDoc msg = "String in parameter is null";
260         final RuntimeException JavaDoc iae = new IllegalArgumentException JavaDoc(msg);
261         throw new RuntimeOperationsException JavaDoc(iae, msg);
262     }
263
264     final String JavaDoc lowerInStr = inStr.toLowerCase();
265     if (!lowerInStr.startsWith("<descriptor>")
266         || !lowerInStr.endsWith("</descriptor>")) {
267         throw new XMLParseException JavaDoc("No <descriptor>, </descriptor> pair");
268     }
269
270     // parse xmlstring into structures
271
init(null);
272     // create dummy descriptor: should have same size
273
// as number of fields in xmlstring
274
// loop through structures and put them in descriptor
275

276     StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(inStr, "<> \t\n\r\f");
277
278     boolean inFld = false;
279     boolean inDesc = false;
280     String JavaDoc fieldName = null;
281     String JavaDoc fieldValue = null;
282
283
284     while (st.hasMoreTokens()) { // loop through tokens
285
String JavaDoc tok = st.nextToken();
286
287         if (tok.equalsIgnoreCase("FIELD")) {
288         inFld = true;
289         } else if (tok.equalsIgnoreCase("/FIELD")) {
290         if ((fieldName != null) && (fieldValue != null)) {
291             fieldName =
292             fieldName.substring(fieldName.indexOf('"') + 1,
293                         fieldName.lastIndexOf('"'));
294             final Object JavaDoc fieldValueObject =
295             parseQuotedFieldValue(fieldValue);
296             setField(fieldName, fieldValueObject);
297         }
298         fieldName = null;
299         fieldValue = null;
300         inFld = false;
301         } else if (tok.equalsIgnoreCase("DESCRIPTOR")) {
302         inDesc = true;
303         } else if (tok.equalsIgnoreCase("/DESCRIPTOR")) {
304         inDesc = false;
305         fieldName = null;
306         fieldValue = null;
307         inFld = false;
308         } else if (inFld && inDesc) {
309         // want kw=value, eg, name="myname" value="myvalue"
310
int eq_separator = tok.indexOf("=");
311         if (eq_separator > 0) {
312             String JavaDoc kwPart = tok.substring(0,eq_separator);
313             String JavaDoc valPart = tok.substring(eq_separator+1);
314             if (kwPart.equalsIgnoreCase("NAME"))
315             fieldName = valPart;
316             else if (kwPart.equalsIgnoreCase("VALUE"))
317             fieldValue = valPart;
318             else { // xml parse exception
319
final String JavaDoc msg =
320                 "Expected `name' or `value', got `" + tok + "'";
321             throw new XMLParseException JavaDoc(msg);
322             }
323         } else { // xml parse exception
324
final String JavaDoc msg =
325             "Expected `keyword=value', got `" + tok + "'";
326             throw new XMLParseException JavaDoc(msg);
327         }
328         }
329     } // while tokens
330

331     if (tracing()) {
332         trace("Descriptor(XMLString)","Exit");
333     }
334     }
335
336     /**
337      * Constructor taking field names and field values. The array and
338      * array elements cannot be null.
339      *
340      * @param fieldNames String array of field names. No elements of
341      * this array can be null.
342      * @param fieldValues Object array of the corresponding field
343      * values. Elements of the array can be null. The
344      * <code>fieldValue</code> must be valid for the
345      * <code>fieldName</code> (as defined in method {@link #isValid
346      * isValid})
347      *
348      * <p>Note: array sizes of parameters should match. If both arrays
349      * are null or empty, then an empty descriptor is created.</p>
350      *
351      * @exception RuntimeOperationsException for illegal value for
352      * field Names or field Values. The array lengths must be equal.
353      * If the descriptor construction fails for any reason, this
354      * exception will be thrown.
355      *
356      */

357     public DescriptorSupport(String JavaDoc[] fieldNames, Object JavaDoc[] fieldValues)
358         throws RuntimeOperationsException JavaDoc {
359     if (tracing()) {
360         trace("Descriptor(fieldNames, fieldObjects)","Constructor");
361     }
362
363     if ((fieldNames == null) || (fieldValues == null) ||
364         (fieldNames.length != fieldValues.length)) {
365         if (tracing()) {
366         trace("Descriptor(String[],Object[])","Illegal arguments");
367         }
368
369         final String JavaDoc msg =
370         "Null or invalid fieldNames or fieldValues";
371         final RuntimeException JavaDoc iae = new IllegalArgumentException JavaDoc(msg);
372         throw new RuntimeOperationsException JavaDoc(iae, msg);
373     }
374
375     /* populate internal structure with fields */
376         init(null);
377     for (int i=0; i < fieldNames.length; i++) {
378         // setField will throw an exception if a fieldName is be null.
379
// the fieldName and fieldValue will be validated in setField.
380
setField(fieldNames[i], fieldValues[i]);
381     }
382     if (tracing()) {
383         trace("Descriptor(fieldNames, fieldObjects)","Exit");
384     }
385     }
386
387     /**
388      * Constructor taking fields in the <i>fieldName=fieldValue</i>
389      * format.
390      *
391      * @param fields String array with each element containing a
392      * field name and value. If this array is null or empty, then the
393      * default constructor will be executed. Null strings or empty
394      * strings will be ignored.
395      *
396      * <p>All field values should be Strings. If the field values are
397      * not Strings, the programmer will have to reset or convert these
398      * fields correctly.
399      *
400      * <p>Note: Each string should be of the form
401      * <i>fieldName=fieldValue</i>.
402      *
403      * @exception RuntimeOperationsException for illegal value for
404      * field Names or field Values. The field must contain an
405      * "=". "=fieldValue", "fieldName", and "fieldValue" are illegal.
406      * FieldName cannot be null. "fieldName=" will cause the value to
407      * be null. If the descriptor construction fails for any reason,
408      * this exception will be thrown.
409      *
410      */

411     public DescriptorSupport(String JavaDoc[] fields)
412     {
413     if (tracing()) {
414         trace("Descriptor(fields)","Constructor");
415     }
416         init(null);
417     if (( fields == null ) || ( fields.length == 0))
418             return;
419
420         init(null);
421
422     for (int i=0; i < fields.length; i++) {
423         if ((fields[i] == null) || (fields[i].equals(""))) {
424         continue;
425         }
426         int eq_separator = fields[i].indexOf("=");
427         if (eq_separator < 0) {
428         // illegal if no = or is first character
429
if (tracing()) {
430             trace("Descriptor(String[])",
431               "Illegal arguments: field does not have '=' " +
432               "as a name and value separator");
433         }
434         final String JavaDoc msg = "Field in invalid format: no equals sign";
435         final RuntimeException JavaDoc iae = new IllegalArgumentException JavaDoc(msg);
436         throw new RuntimeOperationsException JavaDoc(iae, msg);
437         }
438
439         String JavaDoc fieldName = fields[i].substring(0,eq_separator);
440         String JavaDoc fieldValue = null;
441         if (eq_separator < fields[i].length()) {
442         // = is not in last character
443
fieldValue = fields[i].substring(eq_separator+1);
444         }
445
446         if (fieldName.equals("")) {
447         if (tracing()) {
448             trace("Descriptor(String[])",
449               "Illegal arguments: fieldName is empty");
450         }
451
452         final String JavaDoc msg = "Field in invalid format: no fieldName";
453         final RuntimeException JavaDoc iae = new IllegalArgumentException JavaDoc(msg);
454         throw new RuntimeOperationsException JavaDoc(iae, msg);
455         }
456
457         setField(fieldName,fieldValue);
458     }
459     if (tracing()) {
460         trace("Descriptor(fields)","Exit");
461     }
462     }
463     
464     private void init(Map JavaDoc<String JavaDoc, ?> initMap) {
465         descriptorMap =
466                 new TreeMap JavaDoc<String JavaDoc, Object JavaDoc>(String.CASE_INSENSITIVE_ORDER);
467         if (initMap != null)
468             descriptorMap.putAll(initMap);
469     }
470
471     // Implementation of the Descriptor interface
472

473
474     /**
475      * Returns the value for a specific fieldname.
476      *
477      * @param inFieldName The field name in question; if not found,
478      * null is returned.
479      *
480      * @return An Object representing the field value
481      *
482      * @exception RuntimeOperationsException for illegal value (null
483      * or empty string) for field Names.
484      */

485     public synchronized Object JavaDoc getFieldValue(String JavaDoc inFieldName)
486         throws RuntimeOperationsException JavaDoc {
487
488     if ((inFieldName == null) || (inFieldName.equals(""))) {
489         if (tracing()) {
490         trace("getField()","Illegal arguments: null field name.");
491         }
492         final String JavaDoc msg = "Fieldname requested is null";
493         final RuntimeException JavaDoc iae = new IllegalArgumentException JavaDoc(msg);
494         throw new RuntimeOperationsException JavaDoc(iae, msg);
495     }
496     Object JavaDoc retValue = descriptorMap.get(inFieldName);
497     if (tracing()) {
498         trace("getField(" + inFieldName + ")",
499           "Returns '" + retValue + "'");
500     }
501     return(retValue);
502     }
503
504     /**
505      * Sets the string value for a specific fieldname. The value
506      * must be valid for the field (as defined in method {@link
507      * #isValid isValid}). If the field does not exist, it is
508      * added to the Descriptor. If it does exist, the
509      * value is replaced.
510      *
511      * @param inFieldName The field name to be set. Must
512      * not be null or empty string.
513      * @param fieldValue The field value to be set for the field
514      * name. Can be null or empty string.
515      *
516      * @exception RuntimeOperationsException for illegal value for
517      * field Names.
518      *
519      */

520     public synchronized void setField(String JavaDoc inFieldName, Object JavaDoc fieldValue)
521         throws RuntimeOperationsException JavaDoc {
522
523     // field name cannot be null or empty
524
if ((inFieldName == null) || (inFieldName.equals(""))) {
525         if (tracing()) {
526         trace("setField(String,String)",
527               "Illegal arguments: null or empty field name");
528         }
529
530         final String JavaDoc msg = "Fieldname to be set is null or empty";
531         final RuntimeException JavaDoc iae = new IllegalArgumentException JavaDoc(msg);
532         throw new RuntimeOperationsException JavaDoc(iae, msg);
533     }
534
535     if (!validateField(inFieldName, fieldValue)) {
536         if (tracing()) {
537         trace("setField(fieldName,FieldValue)","Illegal arguments");
538         }
539
540         final String JavaDoc msg =
541         "Field value invalid: " + inFieldName + "=" + fieldValue;
542         final RuntimeException JavaDoc iae = new IllegalArgumentException JavaDoc(msg);
543         throw new RuntimeOperationsException JavaDoc(iae, msg);
544     }
545
546     if (tracing()) {
547         if (fieldValue != null) {
548         trace("setField(fieldName, fieldValue)",
549               "Entry: setting '" + inFieldName + "' to '" +
550               fieldValue + "'.");
551         }
552     }
553
554         // Since we do not remove any existing entry with this name,
555
// the field will preserve whatever case it had, ignoring
556
// any difference there might be in inFieldName.
557
descriptorMap.put(inFieldName, fieldValue);
558     }
559
560     /**
561      * Returns all the fields in the descriptor. The order is not the
562      * order in which the fields were set.
563      *
564      * @return String array of fields in the format
565      * <i>fieldName=fieldValue</i>. If there are no fields in the
566      * descriptor, then an empty String array is returned. If a
567      * fieldValue is not a String then the toString() method is called
568      * on it and its returned value is used as the value for the field
569      * enclosed in parenthesis.
570      *
571      * @see #setFields
572      */

573     public synchronized String JavaDoc[] getFields() {
574     if (tracing()) {
575         trace("getFields()","Entry");
576     }
577     int numberOfEntries = descriptorMap.size();
578
579     String JavaDoc[] responseFields = new String JavaDoc[numberOfEntries];
580     Set JavaDoc returnedSet = descriptorMap.entrySet();
581
582     int i = 0;
583     Object JavaDoc currValue = null;
584     Map.Entry JavaDoc currElement = null;
585
586     if (tracing()) {
587         trace("getFields()","Returning " + numberOfEntries + " fields");
588     }
589     for (Iterator JavaDoc iter = returnedSet.iterator(); iter.hasNext(); i++) {
590         currElement = (Map.Entry JavaDoc) iter.next();
591
592         if (currElement == null) {
593         if (tracing()) {
594             trace("getFields()","Element is null");
595         }
596         } else {
597         currValue = currElement.getValue();
598         if (currValue == null) {
599             responseFields[i] = currElement.getKey() + "=";
600         } else {
601             if (currValue instanceof java.lang.String JavaDoc) {
602             responseFields[i] =
603                 currElement.getKey() + "=" + currValue.toString();
604             } else {
605             responseFields[i] =
606                 currElement.getKey() + "=(" +
607                 currValue.toString() + ")";
608             }
609         }
610         }
611     }
612
613     if (tracing()) {
614         trace("getFields()","Exit");
615     }
616
617     return responseFields;
618     }
619
620     /**
621      * Returns all the fields names in the descriptor. The order is
622      * not the order in which the fields were set.
623      *
624      * @return String array of fields names. If the descriptor is
625      * empty, you will get an empty array.
626      *
627      */

628     public synchronized String JavaDoc[] getFieldNames() {
629     if (tracing()) {
630         trace("getFieldNames()","Entry");
631     }
632     int numberOfEntries = descriptorMap.size();
633
634     String JavaDoc[] responseFields = new String JavaDoc[numberOfEntries];
635     Set JavaDoc returnedSet = descriptorMap.entrySet();
636
637     int i = 0;
638
639     if (tracing()) {
640         trace("getFieldNames()","Returning " + numberOfEntries + " fields");
641     }
642
643     for (Iterator JavaDoc iter = returnedSet.iterator(); iter.hasNext(); i++) {
644         Map.Entry JavaDoc currElement = (Map.Entry JavaDoc) iter.next();
645
646         if (( currElement == null ) || (currElement.getKey() == null)) {
647         if (tracing()) {
648             trace("getFieldNames()","Field is null");
649         }
650         } else {
651         responseFields[i] = currElement.getKey().toString();
652         }
653     }
654
655     if (tracing()) {
656         trace("getFieldNames()","Exit");
657     }
658
659     return responseFields;
660     }
661
662
663     /**
664      * Returns all the field values in the descriptor as an array of
665      * Objects. The returned values are in the same order as the
666      * fieldNames String array parameter.
667      *
668      * @param fieldNames String array of the names of the fields that
669      * the values should be returned for.<br>
670      * If the array is empty then an empty array will be returned.<br>
671      * If the array is 'null' then all values will be returned. The
672      * order is not the order in which the fields were set.<br>
673      * If a field name in the array does not exist, then null is
674      * returned for the matching array element being returned.
675      *
676      * @return Object array of field values. If the descriptor is
677      * empty, you will get an empty array.
678      */

679     public synchronized Object JavaDoc[] getFieldValues(String JavaDoc[] fieldNames) {
680     if (tracing()) {
681         trace("getFieldValues(fieldNames)","Entry");
682     }
683     // if fieldNames == null return all values
684
// if fieldNames is String[0] return no values
685

686     int numberOfEntries = descriptorMap.size();
687
688     /* Following test is somewhat inconsistent but is called for
689        by the @return clause above. */

690     if (numberOfEntries == 0)
691         return new Object JavaDoc[0];
692
693     Object JavaDoc[] responseFields;
694     if (fieldNames != null) {
695         responseFields = new Object JavaDoc[fieldNames.length];
696         // room for selected
697
} else {
698         responseFields = new Object JavaDoc[numberOfEntries];
699         // room for all
700
}
701
702     int i = 0;
703
704     if (tracing()) {
705         trace("getFieldValues()",
706           "Returning " + numberOfEntries + " fields");
707     }
708
709     if (fieldNames == null) {
710         for (Iterator JavaDoc iter = descriptorMap.values().iterator();
711          iter.hasNext(); i++)
712         responseFields[i] = iter.next();
713     } else {
714         for (i=0; i < fieldNames.length; i++) {
715         if ((fieldNames[i] == null) || (fieldNames[i].equals(""))) {
716             responseFields[i] = null;
717         } else {
718             responseFields[i] = getFieldValue(fieldNames[i]);
719         }
720         }
721     }
722
723
724     if (tracing()) {
725         trace("getFieldValues()","Exit");
726     }
727
728     return responseFields;
729     }
730
731     /**
732      * Sets all Fields in the list to the new value with the same
733      * index in the fieldValue array. Array sizes must match. The
734      * field value will be validated before it is set (by calling the
735      * method {@link #isValid isValid}). If it is not valid, then an
736      * exception will be thrown. If the arrays are empty, then no
737      * change will take effect.
738      *
739      * @param fieldNames String array of field names. The array and
740      * array elements cannot be null.
741      * @param fieldValues Object array of the corresponding field
742      * values. The array cannot be null. Elements of the array can
743      * be null.
744      *
745      * @exception RuntimeOperationsException for illegal value for
746      * field Names or field Values. Neither can be null. The array
747      * lengths must be equal.
748      *
749      * @see #getFields
750      */

751     public synchronized void setFields(String JavaDoc[] fieldNames,
752                        Object JavaDoc[] fieldValues)
753         throws RuntimeOperationsException JavaDoc {
754
755     if (tracing()) {
756         trace("setFields(fieldNames, ObjectValues)","Entry");
757     }
758
759
760     if ((fieldNames == null) || (fieldValues == null) ||
761         (fieldNames.length != fieldValues.length)) {
762         if (tracing()) {
763         trace("Descriptor.setFields(String[],Object[])",
764               "Illegal arguments");
765         }
766
767         final String JavaDoc msg = "FieldNames and FieldValues are null or invalid";
768         final RuntimeException JavaDoc iae = new IllegalArgumentException JavaDoc(msg);
769         throw new RuntimeOperationsException JavaDoc(iae, msg);
770     }
771
772     for (int i=0; i < fieldNames.length; i++) {
773         if (( fieldNames[i] == null) || (fieldNames[i].equals(""))) {
774         if (tracing()) {
775             trace("Descriptor.setFields(String[],Object[])",
776               "Null field name encountered at " + i + " element");
777         }
778
779         final String JavaDoc msg = "FieldNames is null or invalid";
780         final RuntimeException JavaDoc iae = new IllegalArgumentException JavaDoc(msg);
781         throw new RuntimeOperationsException JavaDoc(iae, msg);
782         }
783         setField(fieldNames[i], fieldValues[i]);
784     }
785     if (tracing()) {
786         trace("Descriptor.setFields(fieldNames, fieldObjects)","Exit");
787     }
788     }
789
790     /**
791      * Returns a new Descriptor which is a duplicate of the Descriptor.
792      *
793      * @exception RuntimeOperationsException for illegal value for
794      * field Names or field Values. If the descriptor construction
795      * fails for any reason, this exception will be thrown.
796      */

797
798     public synchronized Object JavaDoc clone() throws RuntimeOperationsException JavaDoc {
799     if (tracing()) {
800         trace("Descriptor.clone()","Executed");
801     }
802     return(new DescriptorSupport JavaDoc(this));
803     }
804
805     /**
806      * Removes a field from the descriptor.
807      *
808      * @param fieldName String name of the field to be removed.
809      * If the field is not found no exception is thrown.
810      */

811     public synchronized void removeField(String JavaDoc fieldName) {
812     if ((fieldName == null) || (fieldName.equals(""))) {
813         return;
814     }
815
816     descriptorMap.remove(fieldName);
817     }
818
819
820     /**
821      * Returns true if all of the fields have legal values given their
822      * names.
823      * <P>
824      * This implementation does not support interoperating with a directory
825      * or lookup service. Thus, conforming to the specification, no checking is
826      * done on the <i>"export"</i> field.
827      * <P>
828      * Otherwise this implementation returns false if:
829      * <P>
830      * <UL>
831      * <LI> name and descriptorType fieldNames are not defined, or
832      * null, or empty, or not String
833      * <LI> class, role, getMethod, setMethod fieldNames, if defined,
834      * are null or not String
835      * <LI> persistPeriod, currencyTimeLimit, lastUpdatedTimeStamp,
836      * lastReturnedTimeStamp if defined, are null, or not a Numeric
837      * String or not a Numeric Value >= -1
838      * <LI> log fieldName, if defined, is null, or not a Boolean or
839      * not a String with value "t", "f", "true", "false". These String
840      * values must not be case sensitive.
841      * <LI> visibility fieldName, if defined, is null, or not a
842      * Numeric String or a not Numeric Value >= 1 and <= 4
843      * <LI> severity fieldName, if defined, is null, or not a Numeric
844      * String or not a Numeric Value >= 0 and <= 6<br>
845      * <LI> persistPolicy fieldName, if defined, is null, or not a
846      * following String :<br>
847      * "OnUpdate", "OnTimer", "NoMoreOftenThan", "Always",
848      * "Never". These String values must not be case sensitive.<br>
849      * </UL>
850      *
851    &