KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openejb > util > io > ClassDescriptor


1 /**
2  * Redistribution and use of this software and associated documentation
3  * ("Software"), with or without modification, are permitted provided
4  * that the following conditions are met:
5  *
6  * 1. Redistributions of source code must retain copyright
7  * statements and notices. Redistributions must also contain a
8  * copy of this document.
9  *
10  * 2. Redistributions in binary form must reproduce the
11  * above copyright notice, this list of conditions and the
12  * following disclaimer in the documentation and/or other
13  * materials provided with the distribution.
14  *
15  * 3. The name "Exolab" must not be used to endorse or promote
16  * products derived from this Software without prior written
17  * permission of Exoffice Technologies. For written permission,
18  * please contact info@exolab.org.
19  *
20  * 4. Products derived from this Software may not be called "Exolab"
21  * nor may "Exolab" appear in their names without prior written
22  * permission of Exoffice Technologies. Exolab is a registered
23  * trademark of Exoffice Technologies.
24  *
25  * 5. Due credit should be given to the Exolab Project
26  * (http://www.exolab.org/).
27  *
28  * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS
29  * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30  * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
32  * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39  * OF THE POSSIBILITY OF SUCH DAMAGE.
40  *
41  * Copyright 1999 (C) Exoffice Technologies Inc. All Rights Reserved.
42  *
43  * $Id: ClassDescriptor.java 1447 2004-10-25 12:34:45Z pmlopes $
44  */

45 package org.openejb.util.io;
46
47 import java.io.Externalizable JavaDoc;
48 import java.io.IOException JavaDoc;
49 import java.io.ObjectStreamClass JavaDoc;
50 import java.io.ObjectStreamConstants JavaDoc;
51 import java.io.ObjectStreamField JavaDoc;
52 import java.io.Serializable JavaDoc;
53 import java.lang.reflect.AccessibleObject JavaDoc;
54 import java.lang.reflect.Field JavaDoc;
55 import java.lang.reflect.Method JavaDoc;
56 import java.lang.reflect.Modifier JavaDoc;
57 import java.security.AccessController JavaDoc;
58 import java.security.PrivilegedAction JavaDoc;
59 import java.util.Arrays JavaDoc;
60
61 public class ClassDescriptor implements java.io.Serializable JavaDoc, ObjectStreamConstants JavaDoc {
62
63     private static final ObjectStreamField JavaDoc[] serialPersistentFields = ObjectStreamClass.NO_FIELDS;
64
65
66     protected ClassDescriptor(Class JavaDoc clazz, ClassDescriptor superdesc, boolean serializable, boolean externalizable){
67
68         if (externalizable) serializable = false;
69
70         this.forClass = clazz;
71         this.superdesc = superdesc;
72         this.serializable = serializable;
73         this.externalizable = externalizable;
74         this.name = forClass.getName();
75
76         /*
77          * Enter this class in the table of known descriptors.
78          * Otherwise, when the fields are read it may recurse
79          * trying to find the descriptor for itself.
80          */

81         insertDescriptorFor(this);
82
83         if (externalizable) fields = NO_FIELDS;
84         else if (serializable) AccessController.doPrivileged(new AccessibleFieldInitializer(this));
85
86         AccessController.doPrivileged(new SerializationPropertiesReflector(this));
87
88         if (hasWriteObjectMethod) flags |= ObjectStreamConstants.SC_WRITE_METHOD;
89         if (serializable) flags |= ObjectStreamConstants.SC_SERIALIZABLE;
90         if (externalizable) flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
91     }
92
93     protected int flags = 0;
94     protected void writeClassInfo(ObjectOutputStream out) throws IOException JavaDoc{
95         out.writeByte(flags);
96         out.writeShort(fields.length);
97
98         for (int i=0; i < fields.length; i++) fields[i].writeDesc(out);
99     }
100
101     //================================================================
102
// Methods and fields for this classes serializable and
103
// externalizable properties
104
//
105
//====
106

107     private boolean serializable;
108     private boolean externalizable;
109
110     /*
111      * Get the Serializability of the class.
112      */

113     protected boolean isSerializable() {
114         return serializable;
115     }
116
117     /*
118      * Get the externalizability of the class.
119      */

120     protected boolean isExternalizable() {
121         return externalizable;
122     }
123
124     protected boolean isNonSerializable() {
125         return ! (externalizable || serializable);
126     }
127
128
129     //================================================================
130
// Methods and variables to support the fields of this class
131
//
132
//====
133

134     /*
135      * Array of persistent fields of this class, sorted by
136      * type and name.
137      */

138
139     public static final FieldDescriptor[] NO_FIELDS = new FieldDescriptor[0];
140     protected FieldDescriptor[] fields;
141
142     public FieldDescriptor[] getFields(){
143         return fields;
144     }
145
146     public void setFields(FieldDescriptor[] fields){
147         this.fields = fields;
148     }
149
150     //================================================================
151
// Methods to support readObject and writeObject methods
152
// declared by the serializable class.
153
//
154
//====
155

156     private boolean hasWriteObjectMethod;
157     private boolean hasReadObjectMethod;
158     private Method JavaDoc writeObjectMethod;
159     private Method JavaDoc readObjectMethod;
160
161     public boolean hasWriteObjectMethod(){
162         return hasWriteObjectMethod;
163     }
164
165     public void hasWriteObjectMethod(boolean b){
166         hasWriteObjectMethod = b;
167     }
168
169     public Method JavaDoc getWriteObjectMethod(){
170         return writeObjectMethod;
171     }
172
173     protected void setWriteObjectMethod(Method JavaDoc method){
174         writeObjectMethod = method;
175         hasWriteObjectMethod = (method != null);
176     }
177
178     public boolean hasReadObjectMethod(){
179         return hasReadObjectMethod;
180     }
181
182     public void hasReadObjectMethod(boolean b){
183         hasReadObjectMethod = b;
184     }
185
186     public Method JavaDoc getReadObjectMethod(){
187         return readObjectMethod;
188     }
189
190     protected void setReadObjectMethod(Method JavaDoc method){
191         readObjectMethod = method;
192         hasReadObjectMethod = (method != null);
193     }
194
195     //================================================================
196
// Methods and fields to support serialVersionID functionality.
197
//
198
//====
199

200     /*
201      * SerialVersionUID for the class this instance represents.
202      */

203     private long suid;
204
205     /**
206      * Return the serialVersionUID for this class.
207      * NonSerializable classes have a serialVersionUID of 0L.
208      */

209     public long getSerialVersionUID() {
210         return suid;
211     }
212
213     protected void setSerialVersionUID(long suid){
214         this.suid = suid;
215     }
216
217     /*
218      * The name of this descriptor
219      */

220     private String JavaDoc name;
221
222     /**
223      * The name of the class described by this descriptor.
224      */

225     public String JavaDoc getName() {
226         return name;
227     }
228
229     /*
230      * Class that is a descriptor for in this virtual machine.
231      */

232     private Class JavaDoc forClass;
233
234     /**
235      * Return the class in the local VM that this version is mapped to.
236      * Null is returned if there is no corresponding local class.
237      */

238     public Class JavaDoc forClass() {
239         return forClass;
240     }
241
242     /*
243      * The descriptor of the supertype.
244      */

245     private ClassDescriptor superdesc;
246
247     /*
248      * Return the superclass descriptor of this descriptor.
249      */

250     protected ClassDescriptor getSuperclass() {
251         return superdesc;
252     }
253
254     /*
255      * Return the superclass descriptor of this descriptor.
256      */

257     protected void setSuperclass(ClassDescriptor s) {
258         superdesc = s;
259     }
260
261    /**
262      * Return a string describing this ClassDescriptor.
263      */

264     public String JavaDoc toString() {
265         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
266
267         sb.append(name);
268         sb.append(": static final long serialVersionUID = ");
269         sb.append(Long.toString(suid));
270         sb.append("L;");
271         return sb.toString();
272     }
273
274     public static StringBuffer JavaDoc getSignature(Class JavaDoc clazz) {
275         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
276         return getSignature(clazz, buf);
277     }
278
279     public static StringBuffer JavaDoc getSignature(Class JavaDoc clazz, StringBuffer JavaDoc buf) {
280         if (clazz.isPrimitive()) {
281             if (clazz == Integer.TYPE) buf.append('I');
282             else if (clazz == Byte.TYPE) buf.append('B');
283             else if (clazz == Long.TYPE) buf.append('J');
284             else if (clazz == Float.TYPE) buf.append('F');
285             else if (clazz == Double.TYPE) buf.append('D');
286             else if (clazz == Short.TYPE) buf.append('S');
287             else if (clazz == Character.TYPE) buf.append('C');
288             else if (clazz == Boolean.TYPE) buf.append('Z');
289             else if (clazz == Void.TYPE) buf.append('V');
290         }
291         else if (clazz.isArray()) {
292             Class JavaDoc cl = clazz;
293             while (cl.isArray()) {
294                 buf.append('[');
295                 cl = cl.getComponentType();
296             }
297             buf.append(getSignature(cl).toString());
298         }
299         else {
300             buf.append('L');
301             buf.append(clazz.getName().replace('.', '/'));
302             buf.append(';');
303         }
304         return buf;
305     }
306
307     /**
308      * Find the descriptor for a class that can be serialized.
309      * Creates an ObjectStreamClass instance if one does not exist
310      * yet for class. Null is returned if the specified class does not
311      * implement java.io.Serializable or java.io.Externalizable.
312      */

313     public static ClassDescriptor lookup(Class JavaDoc clazz){
314         ClassDescriptor desc = lookupInternal(clazz);
315         if (desc.isSerializable() || desc.isExternalizable()) return desc;
316         return null;
317     }
318
319     /*
320      * Find the class descriptor for the specified class.
321      * Package access only so it can be called from ObjectIn/OutStream.
322      */

323     static ClassDescriptor lookupInternal(Class JavaDoc clazz) {
324         /* Synchronize on the hashtable so no two threads will do
325          * this at the same time.
326          */

327         ClassDescriptor desc = null;
328         synchronized (descriptorFor) {
329             /* Find the matching descriptor if it already known */
330             desc = findDescriptorFor(clazz);
331             if (desc != null) {
332             return desc;
333             }
334
335             /* Check if it's serializable */
336             boolean serializable = Serializable JavaDoc.class.isAssignableFrom(clazz);
337
338             /* If the class is only Serializable,
339              * lookup the descriptor for the superclass.
340              */

341             ClassDescriptor superdesc = null;
342             if (serializable) {
343             Class JavaDoc superclass = clazz.getSuperclass();
344             if (superclass != null)
345                 superdesc = lookup(superclass);
346             }
347
348             /* Check if its' externalizable.
349              * If it's Externalizable, clear the serializable flag.
350              * Only one or the other may be set in the protocol.
351              */

352             boolean externalizable = false;
353             if (serializable) {
354             externalizable =
355                 ((superdesc != null) && superdesc.isExternalizable()) ||
356                 Externalizable JavaDoc.class.isAssignableFrom(clazz);
357             if (externalizable) {
358                 serializable = false;
359             }
360             }
361
362             /* Create a new version descriptor,
363              * it put itself in the known table.
364              */

365             desc = new ClassDescriptor(clazz, superdesc,
366                           serializable, externalizable);
367         }
368         return desc;
369     }
370
371
372
373     /*
374      * findDescriptorFor a Class. This looks in the cache for a
375      * mapping from Class -> ObjectStreamClass mappings. The hashCode
376      * of the Class is used for the lookup since the Class is the key.
377      * The entries are extended from java.lang.ref.SoftReference so the
378      * gc will be able to free them if needed.
379      */

380     private static ClassDescriptor findDescriptorFor(Class JavaDoc clazz) {
381
382         int hash = clazz.hashCode();
383         int index = (hash & 0x7FFFFFFF) % descriptorFor.length;
384         ClassDescriptorEntry e;
385         ClassDescriptorEntry prev;
386
387         /* Free any initial entries whose refs have been cleared */
388         while ((e = descriptorFor[index]) != null && e.get() == null) {
389             descriptorFor[index] = e.next;
390         }
391
392         /* Traverse the chain looking for a descriptor with forClass == clazz.
393          * unlink entries that are unresolved.
394          */

395         prev = e;
396         while (e != null ) {
397             ClassDescriptor desc = (ClassDescriptor)(e.get());
398             if (desc == null) {
399             // This entry has been cleared, unlink it
400
prev.next = e.next;
401             } else {
402             if (desc.forClass == clazz)
403                 return desc;
404             prev = e;
405             }
406             e = e.next;
407         }
408         return null;
409     }
410
411     /*
412      * insertDescriptorFor a Class -> ClassDescriptor mapping.
413      */

414     private static void insertDescriptorFor(ClassDescriptor desc) {
415         // Make sure not already present
416
if (findDescriptorFor(desc.forClass) != null) {
417             return;
418         }
419
420         int hash = desc.forClass.hashCode();
421         int index = (hash & 0x7FFFFFFF) % descriptorFor.length;
422         ClassDescriptorEntry e = new ClassDescriptorEntry(desc);
423         e.next = descriptorFor[index];
424         descriptorFor[index] = e;
425     }
426
427     /*
428      * Entries held in the Cache of known ClassDescriptor objects.
429      * Entries are chained together with the same hash value (modulo array size).
430      */

431     private static class ClassDescriptorEntry extends java.lang.ref.SoftReference JavaDoc {
432         ClassDescriptorEntry next;
433
434         ClassDescriptorEntry(ClassDescriptor c) {
435             super(c);
436         }
437     }
438
439     static private ClassDescriptorEntry[] descriptorFor = new ClassDescriptorEntry[61];
440
441     //================================================================
442
// Inner class extending PrivilegedAction to allow us to relect
443
// with disregard to the modifiers in the class.
444
// This lets us create a ClassDescriptor that represents a completely
445
// accessible version of the Class that can be
446
// read from or written to a srteam.
447
//
448
//
449
//====
450
static final Class JavaDoc[] OIS_ARGS = {java.io.ObjectInputStream JavaDoc.class};
451     static final Class JavaDoc[] OOS_ARGS = {java.io.ObjectOutputStream JavaDoc.class};
452     private class SerializationPropertiesReflector implements PrivilegedAction JavaDoc{
453
454
455         ClassDescriptor desc;
456
457         public SerializationPropertiesReflector(ClassDescriptor desc){
458             this.desc = desc;
459         }
460
461         public Object JavaDoc run() {
462             try {
463                 Field JavaDoc field = null;
464                 field = desc.forClass().getDeclaredField("serialVersionUID");
465                 int modifiers = field.getModifiers();
466                 if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) {
467                     field.setAccessible(true);
468                     desc.setSerialVersionUID(field.getLong(desc.forClass()));
469                 }
470                 else desc.setSerialVersionUID(computeSerialVersionUID(desc.forClass()));
471
472             }
473             catch (NoSuchFieldException JavaDoc ex) { desc.setSerialVersionUID(computeSerialVersionUID(desc.forClass())); }
474             catch (IllegalAccessException JavaDoc ex) { desc.setSerialVersionUID(computeSerialVersionUID(desc.forClass())); }
475
476             if (serializable) {
477                 desc.setWriteObjectMethod(getDeclaredMethod("writeObject",
478                                                             ClassDescriptor.OOS_ARGS,
479                                                             Modifier.PRIVATE,
480                                                             Modifier.STATIC));
481
482                 desc.setWriteObjectMethod(getDeclaredMethod("readObject",
483                                                             ClassDescriptor.OIS_ARGS,
484                                                             Modifier.PRIVATE,
485                                                             Modifier.STATIC));
486             }
487             return null;
488         }
489
490         private Method JavaDoc getDeclaredMethod(String JavaDoc methodName, Class JavaDoc[] args, int requiredModifierMask, int disallowedModifierMask) {
491             Method JavaDoc method = null;
492             try {
493                 method = forClass.getDeclaredMethod(methodName, args);
494                 if (method != null) {
495                     int mods = method.getModifiers();
496                     if ((mods & disallowedModifierMask) != 0 || (mods & requiredModifierMask) != requiredModifierMask)
497                          method = null;
498                     else method.setAccessible(true);
499
500                 }
501             }
502             catch (NoSuchMethodException JavaDoc e) { /* This is not a problem. */ }
503             return method;
504         }
505
506         private long computeSerialVersionUID(Class JavaDoc clazz){
507             /* In this implementation we are not using the
508              * serialVersionID. The classes on both ends must be compatable.
509              */

510              return 42L;
511         }
512
513     }
514
515     //================================================================
516
// Inner class extending PrivilegedAction to allow us to relect
517
// with disregard to the modifiers in the class.
518
// This lets us create a ClassDescriptor that represents a completely
519
// accessible version of the Class that can be
520
// read from or written to a srteam.
521
//
522
//
523
//====
524
private static class AccessibleFieldInitializer implements PrivilegedAction JavaDoc{
525
526         ClassDescriptor desc;
527
528         public AccessibleFieldInitializer(ClassDescriptor desc){
529             this.desc = desc;
530         }
531
532
533         public Object JavaDoc run() {
534             FieldDescriptor[] fields = getDeclaredSerialPersistentFields(desc.forClass());
535             if (fields == null) fields = reflectForFields(desc.forClass());
536
537             if (fields.length > 1) Arrays.sort(fields);
538
539             desc.setFields(fields);
540             return null;
541         }
542
543         private FieldDescriptor[] getDeclaredSerialPersistentFields(Class JavaDoc clazz) throws SecurityException JavaDoc{
544             java.lang.reflect.Field JavaDoc[] tmpFields = null;
545             ObjectStreamField JavaDoc[] outputStreamFields;
546
547             try {
548                 Field JavaDoc serialPersistentFields = clazz.getDeclaredField("serialPersistentFields");
549                 /* This field must be private according to the spec */
550                 if (!Modifier.isPrivate(serialPersistentFields.getModifiers())) return null;
551
552                 serialPersistentFields.setAccessible(true);
553
554                 /* Safely retreive the value of the class' serialPersistentFields.
555                  * These fields are of the wrong type they are of the type
556                  * java.io.ObjectStreamField and are nothing more than wrappers
557                  * for a methods name and type to anyone outside the java.io package
558                  * For this reason we must create our own more accessible FieldDescriptors
559                  * that encompas the functionality we need.
560                  */

561
562                 outputStreamFields = (ObjectStreamField JavaDoc[])serialPersistentFields.get(clazz);
563                 tmpFields = new Field JavaDoc[outputStreamFields.length];
564
565             }
566             catch (NoSuchFieldException JavaDoc e) { return null; }
567             catch (IllegalAccessException JavaDoc e) { return null; }
568             catch (IllegalArgumentException JavaDoc e) { return null; }
569
570             /* Validate the existence of the fields the class indicated
571              * and instaniate a FieldDescriptor for it.
572              */

573             Field JavaDoc reflectedField;
574             int validFields = 0;
575             for (int i = outputStreamFields.length-1; i >= 0; i--){
576                 try {
577                     reflectedField = clazz.getDeclaredField(outputStreamFields[i].getName());
578                     if (outputStreamFields[i].getType() == reflectedField.getType()) {
579                         reflectedField.setAccessible(true);
580                         tmpFields[validFields++] = reflectedField;
581                     }
582                 }
583                 catch (NoSuchFieldException JavaDoc e) { /* unimportant. keep going */}
584             }
585
586             FieldDescriptor[] validFieldDescriptors = new FieldDescriptor[validFields];
587
588             for (validFields-- ; validFields >= 0; validFields--){
589                 validFieldDescriptors[validFields] = new FieldDescriptor(tmpFields[validFields]);
590             }
591
592             return validFieldDescriptors;
593         }
594
595         /**
596          * Use reflection to gather a list of all the fields in the class
597          */

598         private FieldDescriptor[] reflectForFields(Class JavaDoc clazz) {
599
600             Field JavaDoc[] allFields = clazz.getDeclaredFields();
601             AccessibleObject.setAccessible(allFields, true);
602             Field JavaDoc[] tmpFields = new Field JavaDoc[allFields.length];
603
604             int validFields = 0, modifiers;
605
606             for (int i = 0; i < allFields.length; i++) {
607                 modifiers = allFields[i].getModifiers();
608                 if (!Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers))
609                     tmpFields[validFields++] = allFields[i];
610             }
611
612             FieldDescriptor[] validFieldDescriptors = new FieldDescriptor[validFields];
613
614             for (validFields-- ; validFields >= 0; validFields--){
615                 validFieldDescriptors[validFields] = new FieldDescriptor(tmpFields[validFields]);
616             }
617
618             return validFieldDescriptors;
619         }
620     }
621
622 }
Popular Tags