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