1:
38:
39:
40: package ;
41:
42: import ;
43: import ;
44: import ;
45: import ;
46:
47: import ;
48: import ;
49: import ;
50: import ;
51: import ;
52: import ;
53: import ;
54: import ;
55: import ;
56: import ;
57: import ;
58: import ;
59: import ;
60: import ;
61: import ;
62: import ;
63:
64: public class ObjectStreamClass implements Serializable
65: {
66:
76: public static ObjectStreamClass lookup(Class cl)
77: {
78: if (cl == null)
79: return null;
80: if (! (Serializable.class).isAssignableFrom(cl))
81: return null;
82:
83: return lookupForClassObject(cl);
84: }
85:
86:
91: static ObjectStreamClass lookupForClassObject(Class cl)
92: {
93: if (cl == null)
94: return null;
95:
96: ObjectStreamClass osc = (ObjectStreamClass) classLookupTable.get(cl);
97:
98: if (osc != null)
99: return osc;
100: else
101: {
102: osc = new ObjectStreamClass(cl);
103: classLookupTable.put(cl, osc);
104: return osc;
105: }
106: }
107:
108:
114: public String getName()
115: {
116: return name;
117: }
118:
119:
128: public Class forClass()
129: {
130: return clazz;
131: }
132:
133:
142: public long getSerialVersionUID()
143: {
144: return uid;
145: }
146:
147:
154: public ObjectStreamField[] getFields()
155: {
156: ObjectStreamField[] copy = new ObjectStreamField[ fields.length ];
157: System.arraycopy(fields, 0, copy, 0, fields.length);
158: return copy;
159: }
160:
161:
162:
163:
164: public ObjectStreamField getField (String name)
165: {
166: for (int i = 0; i < fields.length; i++)
167: if (fields[i].getName().equals(name))
168: return fields[i];
169: return null;
170: }
171:
172:
181: public String toString()
182: {
183: return "java.io.ObjectStreamClass< " + name + ", " + uid + " >";
184: }
185:
186:
187:
188:
189:
190:
191:
192:
193: boolean hasWriteMethod()
194: {
195: return (flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0;
196: }
197:
198:
199:
200: boolean isSerializable()
201: {
202: return (flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0;
203: }
204:
205:
206:
207:
208: boolean isExternalizable()
209: {
210: return (flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0;
211: }
212:
213:
214:
215:
216:
217:
218: ObjectStreamClass getSuper()
219: {
220: return superClass;
221: }
222:
223:
224:
225:
226:
227:
228: static ObjectStreamClass[] getObjectStreamClasses(Class clazz)
229: {
230: ObjectStreamClass osc = ObjectStreamClass.lookup(clazz);
231:
232: if (osc == null)
233: return new ObjectStreamClass[0];
234: else
235: {
236: Vector oscs = new Vector();
237:
238: while (osc != null)
239: {
240: oscs.addElement (osc);
241: osc = osc.getSuper();
242: }
243:
244: int count = oscs.size();
245: ObjectStreamClass[] sorted_oscs = new ObjectStreamClass[ count ];
246:
247: for (int i = count - 1; i >= 0; i--)
248: sorted_oscs[ count - i - 1 ] = (ObjectStreamClass) oscs.elementAt(i);
249:
250: return sorted_oscs;
251: }
252: }
253:
254:
255:
256:
257:
258:
259: int getFlags()
260: {
261: return flags;
262: }
263:
264:
265: ObjectStreamClass(String name, long uid, byte flags,
266: ObjectStreamField[] fields)
267: {
268: this.name = name;
269: this.uid = uid;
270: this.flags = flags;
271: this.fields = fields;
272: }
273:
274:
285: void setClass(Class cl, ObjectStreamClass superClass) throws InvalidClassException
286: {
287: this.clazz = cl;
288:
289: cacheMethods();
290:
291: long class_uid = getClassUID(cl);
292: if (uid == 0)
293: uid = class_uid;
294: else
295: {
296:
297:
298: if (uid != class_uid)
299: {
300: String msg = cl +
301: ": Local class not compatible: stream serialVersionUID="
302: + uid + ", local serialVersionUID=" + class_uid;
303: throw new InvalidClassException (msg);
304: }
305: }
306:
307: isProxyClass = clazz != null && Proxy.isProxyClass(clazz);
308: this.superClass = superClass;
309: calculateOffsets();
310:
311: try
312: {
313: ObjectStreamField[] exportedFields = getSerialPersistentFields (clazz);
314:
315: if (exportedFields == null)
316: return;
317:
318: ObjectStreamField[] newFieldList = new ObjectStreamField[exportedFields.length + fields.length];
319: int i, j, k;
320:
321:
325:
326: Arrays.sort(exportedFields);
327:
328: i = 0; j = 0; k = 0;
329: while (i < fields.length && j < exportedFields.length)
330: {
331: int comp = fields[i].compareTo(exportedFields[j]);
332:
333: if (comp < 0)
334: {
335: newFieldList[k] = fields[i];
336: fields[i].setPersistent(false);
337: fields[i].setToSet(false);
338: i++;
339: }
340: else if (comp > 0)
341: {
342:
345: newFieldList[k] = exportedFields[j];
346: newFieldList[k].setPersistent(true);
347: newFieldList[k].setToSet(false);
348: try
349: {
350: newFieldList[k].lookupField(clazz);
351: newFieldList[k].checkFieldType();
352: }
353: catch (NoSuchFieldException _)
354: {
355: }
356: j++;
357: }
358: else
359: {
360: try
361: {
362: exportedFields[j].lookupField(clazz);
363: exportedFields[j].checkFieldType();
364: }
365: catch (NoSuchFieldException _)
366: {
367: }
368:
369: if (!fields[i].getType().equals(exportedFields[j].getType()))
370: throw new InvalidClassException
371: ("serialPersistentFields must be compatible with" +
372: " imported fields (about " + fields[i].getName() + ")");
373: newFieldList[k] = fields[i];
374: fields[i].setPersistent(true);
375: i++;
376: j++;
377: }
378: k++;
379: }
380:
381: if (i < fields.length)
382: for (;i<fields.length;i++,k++)
383: {
384: fields[i].setPersistent(false);
385: fields[i].setToSet(false);
386: newFieldList[k] = fields[i];
387: }
388: else
389: if (j < exportedFields.length)
390: for (;j<exportedFields.length;j++,k++)
391: {
392: exportedFields[j].setPersistent(true);
393: exportedFields[j].setToSet(false);
394: newFieldList[k] = exportedFields[j];
395: }
396:
397: fields = new ObjectStreamField[k];
398: System.arraycopy(newFieldList, 0, fields, 0, k);
399: }
400: catch (NoSuchFieldException ignore)
401: {
402: return;
403: }
404: catch (IllegalAccessException ignore)
405: {
406: return;
407: }
408: }
409:
410: void setSuperclass (ObjectStreamClass osc)
411: {
412: superClass = osc;
413: }
414:
415: void calculateOffsets()
416: {
417: int i;
418: ObjectStreamField field;
419: primFieldSize = 0;
420: int fcount = fields.length;
421: for (i = 0; i < fcount; ++ i)
422: {
423: field = fields[i];
424:
425: if (! field.isPrimitive())
426: break;
427:
428: field.setOffset(primFieldSize);
429: switch (field.getTypeCode())
430: {
431: case 'B':
432: case 'Z':
433: ++ primFieldSize;
434: break;
435: case 'C':
436: case 'S':
437: primFieldSize += 2;
438: break;
439: case 'I':
440: case 'F':
441: primFieldSize += 4;
442: break;
443: case 'D':
444: case 'J':
445: primFieldSize += 8;
446: break;
447: }
448: }
449:
450: for (objectFieldCount = 0; i < fcount; ++ i)
451: fields[i].setOffset(objectFieldCount++);
452: }
453:
454: private Method findMethod(Method[] methods, String name, Class[] params,
455: Class returnType, boolean mustBePrivate)
456: {
457: outer:
458: for (int i = 0; i < methods.length; i++)
459: {
460: final Method m = methods[i];
461: int mods = m.getModifiers();
462: if (Modifier.isStatic(mods)
463: || (mustBePrivate && !Modifier.isPrivate(mods)))
464: {
465: continue;
466: }
467:
468: if (m.getName().equals(name)
469: && m.getReturnType() == returnType)
470: {
471: Class[] mp = m.getParameterTypes();
472: if (mp.length == params.length)
473: {
474: for (int j = 0; j < mp.length; j++)
475: {
476: if (mp[j] != params[j])
477: {
478: continue outer;
479: }
480: }
481: AccessController.doPrivileged(new SetAccessibleAction(m));
482: return m;
483: }
484: }
485: }
486: return null;
487: }
488:
489: private static boolean inSamePackage(Class c1, Class c2)
490: {
491: String name1 = c1.getName();
492: String name2 = c2.getName();
493:
494: int id1 = name1.lastIndexOf('.');
495: int id2 = name2.lastIndexOf('.');
496:
497:
498: if (id1 == -1 || id2 == -1)
499: return id1 == id2;
500:
501: String package1 = name1.substring(0, id1);
502: String package2 = name2.substring(0, id2);
503:
504: return package1.equals(package2);
505: }
506:
507: final static Class[] noArgs = new Class[0];
508:
509: private static Method findAccessibleMethod(String name, Class from)
510: {
511: for (Class c = from; c != null; c = c.getSuperclass())
512: {
513: try
514: {
515: Method res = c.getDeclaredMethod(name, noArgs);
516: int mods = res.getModifiers();
517:
518: if (c != from
519: && (Modifier.isPrivate(mods)
520: || ! Modifier.isPublic(mods) && ! inSamePackage(c, from)))
521: continue;
522:
523: AccessController.doPrivileged(new SetAccessibleAction(res));
524: return res;
525: }
526: catch (NoSuchMethodException e)
527: {
528: }
529: }
530:
531: return null;
532: }
533:
534: private void cacheMethods()
535: {
536: Method[] methods = forClass().getDeclaredMethods();
537:
538: readObjectMethod = findMethod(methods, "readObject",
539: new Class[] { ObjectInputStream.class },
540: Void.TYPE, true);
541: writeObjectMethod = findMethod(methods, "writeObject",
542: new Class[] { ObjectOutputStream.class },
543: Void.TYPE, true);
544:
545:
546:
547: readResolveMethod = findAccessibleMethod("readResolve", forClass());
548: writeReplaceMethod = findAccessibleMethod("writeReplace", forClass());
549: }
550:
551: private ObjectStreamClass(Class cl)
552: {
553: uid = 0;
554: flags = 0;
555: isProxyClass = Proxy.isProxyClass(cl);
556:
557: clazz = cl;
558: cacheMethods();
559: name = cl.getName();
560: setFlags(cl);
561: setFields(cl);
562:
563: if ( (Serializable.class).isAssignableFrom(cl) && !isProxyClass)
564: uid = getClassUID(cl);
565: superClass = lookup(cl.getSuperclass());
566: }
567:
568:
569:
570: private void setFlags(Class cl)
571: {
572: if ((java.io.Externalizable.class).isAssignableFrom(cl))
573: flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
574: else if ((java.io.Serializable.class).isAssignableFrom(cl))
575:
576: flags |= ObjectStreamConstants.SC_SERIALIZABLE;
577:
578: if (writeObjectMethod != null)
579: flags |= ObjectStreamConstants.SC_WRITE_METHOD;
580: }
581:
582:
583:
584:
585: private void setFields(Class cl)
586: {
587: SetAccessibleAction setAccessible = new SetAccessibleAction();
588:
589: if (!isSerializable() || isExternalizable())
590: {
591: fields = NO_FIELDS;
592: return;
593: }
594:
595: try
596: {
597: final Field f =
598: cl.getDeclaredField("serialPersistentFields");
599: setAccessible.setMember(f);
600: AccessController.doPrivileged(setAccessible);
601: int modifiers = f.getModifiers();
602:
603: if (Modifier.isStatic(modifiers)
604: && Modifier.isFinal(modifiers)
605: && Modifier.isPrivate(modifiers))
606: {
607: fields = getSerialPersistentFields(cl);
608: if (fields != null)
609: {
610: Arrays.sort (fields);
611:
612: for (int i=0; i < fields.length; i++)
613: {
614: try
615: {
616: fields[i].lookupField(cl);
617: }
618: catch (NoSuchFieldException _)
619: {
620: fields[i].setToSet(false);
621: }
622: }
623:
624: calculateOffsets();
625: return;
626: }
627: }
628: }
629: catch (NoSuchFieldException ignore)
630: {
631: }
632: catch (IllegalAccessException ignore)
633: {
634: }
635:
636: int num_good_fields = 0;
637: Field[] all_fields = cl.getDeclaredFields();
638:
639: int modifiers;
640:
641: for (int i = 0; i < all_fields.length; i++)
642: {
643: modifiers = all_fields[i].getModifiers();
644: if (Modifier.isTransient(modifiers)
645: || Modifier.isStatic(modifiers))
646: all_fields[i] = null;
647: else
648: num_good_fields++;
649: }
650:
651:
652: fields = new ObjectStreamField[ num_good_fields ];
653: for (int from = 0, to = 0; from < all_fields.length; from++)
654: if (all_fields[from] != null)
655: {
656: final Field f = all_fields[from];
657: setAccessible.setMember(f);
658: AccessController.doPrivileged(setAccessible);
659: fields[to] = new ObjectStreamField(all_fields[from]);
660: to++;
661: }
662:
663: Arrays.sort(fields);
664:
665:
666: for (int i = 1; i < fields.length; i++)
667: {
668: if(fields[i - 1].getName().equals(fields[i].getName()))
669: throw new InternalError("Duplicate field " +
670: fields[i].getName() + " in class " + cl.getName());
671: }
672: calculateOffsets();
673: }
674:
675:
676:
677: private long getClassUID(Class cl)
678: {
679: try
680: {
681:
682:
683:
684: final Field suid = cl.getDeclaredField("serialVersionUID");
685: SetAccessibleAction setAccessible = new SetAccessibleAction(suid);
686: AccessController.doPrivileged(setAccessible);
687: int modifiers = suid.getModifiers();
688:
689: if (Modifier.isStatic(modifiers)
690: && Modifier.isFinal(modifiers)
691: && suid.getType() == Long.TYPE)
692: return suid.getLong(null);
693: }
694: catch (NoSuchFieldException ignore)
695: {
696: }
697: catch (IllegalAccessException ignore)
698: {
699: }
700:
701:
702: try
703: {
704: MessageDigest md;
705: try
706: {
707: md = MessageDigest.getInstance("SHA");
708: }
709: catch (NoSuchAlgorithmException e)
710: {
711:
712: Gnu gnuProvider = new Gnu();
713: Security.addProvider(gnuProvider);
714: md = MessageDigest.getInstance("SHA");
715: }
716:
717: DigestOutputStream digest_out =
718: new DigestOutputStream(nullOutputStream, md);
719: DataOutputStream data_out = new DataOutputStream(digest_out);
720:
721: data_out.writeUTF(cl.getName());
722:
723: int modifiers = cl.getModifiers();
724:
725: modifiers = modifiers & (Modifier.ABSTRACT | Modifier.FINAL
726: | Modifier.INTERFACE | Modifier.PUBLIC);
727: data_out.writeInt(modifiers);
728:
729:
730:
731: if (! cl.isArray())
732: {
733: Class[] interfaces = cl.getInterfaces();
734: Arrays.sort(interfaces, interfaceComparator);
735: for (int i = 0; i < interfaces.length; i++)
736: data_out.writeUTF(interfaces[i].getName());
737: }
738:
739: Field field;
740: Field[] fields = cl.getDeclaredFields();
741: Arrays.sort(fields, memberComparator);
742: for (int i = 0; i < fields.length; i++)
743: {
744: field = fields[i];
745: modifiers = field.getModifiers();
746: if (Modifier.isPrivate(modifiers)
747: && (Modifier.isStatic(modifiers)
748: || Modifier.isTransient(modifiers)))
749: continue;
750:
751: data_out.writeUTF(field.getName());
752: data_out.writeInt(modifiers);
753: data_out.writeUTF(TypeSignature.getEncodingOfClass (field.getType()));
754: }
755:
756:
757: if (VMObjectStreamClass.hasClassInitializer(cl))
758: {
759: data_out.writeUTF("<clinit>");
760: data_out.writeInt(Modifier.STATIC);
761: data_out.writeUTF("()V");
762: }
763:
764: Constructor constructor;
765: Constructor[] constructors = cl.getDeclaredConstructors();
766: Arrays.sort (constructors, memberComparator);
767: for (int i = 0; i < constructors.length; i++)
768: {
769: constructor = constructors[i];
770: modifiers = constructor.getModifiers();
771: if (Modifier.isPrivate(modifiers))
772: continue;
773:
774: data_out.writeUTF("<init>");
775: data_out.writeInt(modifiers);
776:
777:
778:
779: data_out.writeUTF
780: (TypeSignature.getEncodingOfConstructor(constructor).replace('/','.'));
781: }
782:
783: Method method;
784: Method[] methods = cl.getDeclaredMethods();
785: Arrays.sort(methods, memberComparator);
786: for (int i = 0; i < methods.length; i++)
787: {
788: method = methods[i];
789: modifiers = method.getModifiers();
790: if (Modifier.isPrivate(modifiers))
791: continue;
792:
793: data_out.writeUTF(method.getName());
794: data_out.writeInt(modifiers);
795:
796:
797:
798: data_out.writeUTF
799: (TypeSignature.getEncodingOfMethod(method).replace('/', '.'));
800: }
801:
802: data_out.close();
803: byte[] sha = md.digest();
804: long result = 0;
805: int len = sha.length < 8 ? sha.length : 8;
806: for (int i = 0; i < len; i++)
807: result += (long) (sha[i] & 0xFF) << (8 * i);
808:
809: return result;
810: }
811: catch (NoSuchAlgorithmException e)
812: {
813: throw new RuntimeException
814: ("The SHA algorithm was not found to use in computing the Serial Version UID for class "
815: + cl.getName(), e);
816: }
817: catch (IOException ioe)
818: {
819: throw new RuntimeException(ioe);
820: }
821: }
822:
823:
832: private ObjectStreamField[] getSerialPersistentFields(Class clazz)
833: throws NoSuchFieldException, IllegalAccessException
834: {
835: ObjectStreamField[] fieldsArray = null;
836: ObjectStreamField[] o;
837:
838:
839:
840: Field f = clazz.getDeclaredField("serialPersistentFields");
841: f.setAccessible(true);
842:
843: int modifiers = f.getModifiers();
844: if (!(Modifier.isStatic(modifiers) &&
845: Modifier.isFinal(modifiers) &&
846: Modifier.isPrivate(modifiers)))
847: return null;
848:
849: o = (ObjectStreamField[]) f.get(null);
850:
851: if (o == null)
852: return null;
853:
854: fieldsArray = new ObjectStreamField[ o.length ];
855: System.arraycopy(o, 0, fieldsArray, 0, o.length);
856:
857: return fieldsArray;
858: }
859:
860:
867: Externalizable newInstance() throws InvalidClassException
868: {
869: synchronized(this)
870: {
871: if (constructor == null)
872: {
873: try
874: {
875: final Constructor c = clazz.getConstructor(new Class[0]);
876:
877: AccessController.doPrivileged(new PrivilegedAction()
878: {
879: public Object run()
880: {
881: c.setAccessible(true);
882: return null;
883: }
884: });
885:
886: constructor = c;
887: }
888: catch(NoSuchMethodException x)
889: {
890: throw new InvalidClassException(clazz.getName(),
891: "No public zero-argument constructor");
892: }
893: }
894: }
895:
896: try
897: {
898: return (Externalizable)constructor.newInstance(null);
899: }
900: catch(Exception x)
901: {
902: throw (InvalidClassException)
903: new InvalidClassException(clazz.getName(),
904: "Unable to instantiate").initCause(x);
905: }
906: }
907:
908: public static final ObjectStreamField[] NO_FIELDS = {};
909:
910: private static Hashtable classLookupTable = new Hashtable();
911: private static final NullOutputStream nullOutputStream = new NullOutputStream();
912: private static final Comparator interfaceComparator = new InterfaceComparator();
913: private static final Comparator memberComparator = new MemberComparator();
914: private static final
915: Class[] writeMethodArgTypes = { java.io.ObjectOutputStream.class };
916:
917: private ObjectStreamClass superClass;
918: private Class clazz;
919: private String name;
920: private long uid;
921: private byte flags;
922:
923:
924:
925: ObjectStreamField[] fields;
926:
927:
928: int primFieldSize = -1;
929: int objectFieldCount;
930:
931: Method readObjectMethod;
932: Method readResolveMethod;
933: Method writeReplaceMethod;
934: Method writeObjectMethod;
935: boolean realClassIsSerializable;
936: boolean realClassIsExternalizable;
937: ObjectStreamField[] fieldMapping;
938: Constructor firstNonSerializableParentConstructor;
939: private Constructor constructor;
940:
941: boolean isProxyClass = false;
942:
943:
944:
945: private static final long serialVersionUID = -6120832682080437368L;
946:
947:
948:
949: private static final class InterfaceComparator implements Comparator
950: {
951: public int compare(Object o1, Object o2)
952: {
953: return ((Class) o1).getName().compareTo(((Class) o2).getName());
954: }
955: }
956:
957:
958:
959:
960: private static final class MemberComparator implements Comparator
961: {
962: public int compare(Object o1, Object o2)
963: {
964: Member m1 = (Member) o1;
965: Member m2 = (Member) o2;
966:
967: int comp = m1.getName().compareTo(m2.getName());
968:
969: if (comp == 0)
970: return TypeSignature.getEncodingOfMember(m1).
971: compareTo(TypeSignature.getEncodingOfMember(m2));
972: else
973: return comp;
974: }
975: }
976: }