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.isProtected(mods)
520: || Modifier.isPublic(mods)
521: || (! Modifier.isPrivate(mods) && inSamePackage(c, from)))
522: {
523: AccessController.doPrivileged(new SetAccessibleAction(res));
524: return res;
525: }
526: }
527: catch (NoSuchMethodException e)
528: {
529: }
530: }
531:
532: return null;
533: }
534:
535: private void cacheMethods()
536: {
537: Method[] methods = forClass().getDeclaredMethods();
538:
539: readObjectMethod = findMethod(methods, "readObject",
540: new Class[] { ObjectInputStream.class },
541: Void.TYPE, true);
542: writeObjectMethod = findMethod(methods, "writeObject",
543: new Class[] { ObjectOutputStream.class },
544: Void.TYPE, true);
545:
546:
547:
548: readResolveMethod = findAccessibleMethod("readResolve", forClass());
549: writeReplaceMethod = findAccessibleMethod("writeReplace", forClass());
550: }
551:
552: private ObjectStreamClass(Class cl)
553: {
554: uid = 0;
555: flags = 0;
556: isProxyClass = Proxy.isProxyClass(cl);
557:
558: clazz = cl;
559: cacheMethods();
560: name = cl.getName();
561: setFlags(cl);
562: setFields(cl);
563:
564: if ( (Serializable.class).isAssignableFrom(cl) && !isProxyClass)
565: uid = getClassUID(cl);
566: superClass = lookup(cl.getSuperclass());
567: }
568:
569:
570:
571: private void setFlags(Class cl)
572: {
573: if ((java.io.Externalizable.class).isAssignableFrom(cl))
574: flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
575: else if ((java.io.Serializable.class).isAssignableFrom(cl))
576:
577: flags |= ObjectStreamConstants.SC_SERIALIZABLE;
578:
579: if (writeObjectMethod != null)
580: flags |= ObjectStreamConstants.SC_WRITE_METHOD;
581: }
582:
583:
584:
585:
586: private void setFields(Class cl)
587: {
588: SetAccessibleAction setAccessible = new SetAccessibleAction();
589:
590: if (!isSerializable() || isExternalizable())
591: {
592: fields = NO_FIELDS;
593: return;
594: }
595:
596: try
597: {
598: final Field f =
599: cl.getDeclaredField("serialPersistentFields");
600: setAccessible.setMember(f);
601: AccessController.doPrivileged(setAccessible);
602: int modifiers = f.getModifiers();
603:
604: if (Modifier.isStatic(modifiers)
605: && Modifier.isFinal(modifiers)
606: && Modifier.isPrivate(modifiers))
607: {
608: fields = getSerialPersistentFields(cl);
609: if (fields != null)
610: {
611: Arrays.sort (fields);
612:
613: for (int i=0; i < fields.length; i++)
614: {
615: try
616: {
617: fields[i].lookupField(cl);
618: }
619: catch (NoSuchFieldException _)
620: {
621: fields[i].setToSet(false);
622: }
623: }
624:
625: calculateOffsets();
626: return;
627: }
628: }
629: }
630: catch (NoSuchFieldException ignore)
631: {
632: }
633: catch (IllegalAccessException ignore)
634: {
635: }
636:
637: int num_good_fields = 0;
638: Field[] all_fields = cl.getDeclaredFields();
639:
640: int modifiers;
641:
642: for (int i = 0; i < all_fields.length; i++)
643: {
644: modifiers = all_fields[i].getModifiers();
645: if (Modifier.isTransient(modifiers)
646: || Modifier.isStatic(modifiers))
647: all_fields[i] = null;
648: else
649: num_good_fields++;
650: }
651:
652:
653: fields = new ObjectStreamField[ num_good_fields ];
654: for (int from = 0, to = 0; from < all_fields.length; from++)
655: if (all_fields[from] != null)
656: {
657: final Field f = all_fields[from];
658: setAccessible.setMember(f);
659: AccessController.doPrivileged(setAccessible);
660: fields[to] = new ObjectStreamField(all_fields[from]);
661: to++;
662: }
663:
664: Arrays.sort(fields);
665:
666:
667: for (int i = 1; i < fields.length; i++)
668: {
669: if(fields[i - 1].getName().equals(fields[i].getName()))
670: throw new InternalError("Duplicate field " +
671: fields[i].getName() + " in class " + cl.getName());
672: }
673: calculateOffsets();
674: }
675:
676:
677:
678: private long getClassUID(Class cl)
679: {
680: try
681: {
682:
683:
684:
685: final Field suid = cl.getDeclaredField("serialVersionUID");
686: SetAccessibleAction setAccessible = new SetAccessibleAction(suid);
687: AccessController.doPrivileged(setAccessible);
688: int modifiers = suid.getModifiers();
689:
690: if (Modifier.isStatic(modifiers)
691: && Modifier.isFinal(modifiers)
692: && suid.getType() == Long.TYPE)
693: return suid.getLong(null);
694: }
695: catch (NoSuchFieldException ignore)
696: {
697: }
698: catch (IllegalAccessException ignore)
699: {
700: }
701:
702:
703: try
704: {
705: MessageDigest md;
706: try
707: {
708: md = MessageDigest.getInstance("SHA");
709: }
710: catch (NoSuchAlgorithmException e)
711: {
712:
713: Gnu gnuProvider = new Gnu();
714: Security.addProvider(gnuProvider);
715: md = MessageDigest.getInstance("SHA");
716: }
717:
718: DigestOutputStream digest_out =
719: new DigestOutputStream(nullOutputStream, md);
720: DataOutputStream data_out = new DataOutputStream(digest_out);
721:
722: data_out.writeUTF(cl.getName());
723:
724: int modifiers = cl.getModifiers();
725:
726: modifiers = modifiers & (Modifier.ABSTRACT | Modifier.FINAL
727: | Modifier.INTERFACE | Modifier.PUBLIC);
728: data_out.writeInt(modifiers);
729:
730:
731:
732: if (! cl.isArray())
733: {
734: Class[] interfaces = cl.getInterfaces();
735: Arrays.sort(interfaces, interfaceComparator);
736: for (int i = 0; i < interfaces.length; i++)
737: data_out.writeUTF(interfaces[i].getName());
738: }
739:
740: Field field;
741: Field[] fields = cl.getDeclaredFields();
742: Arrays.sort(fields, memberComparator);
743: for (int i = 0; i < fields.length; i++)
744: {
745: field = fields[i];
746: modifiers = field.getModifiers();
747: if (Modifier.isPrivate(modifiers)
748: && (Modifier.isStatic(modifiers)
749: || Modifier.isTransient(modifiers)))
750: continue;
751:
752: data_out.writeUTF(field.getName());
753: data_out.writeInt(modifiers);
754: data_out.writeUTF(TypeSignature.getEncodingOfClass (field.getType()));
755: }
756:
757:
758: if (VMObjectStreamClass.hasClassInitializer(cl))
759: {
760: data_out.writeUTF("<clinit>");
761: data_out.writeInt(Modifier.STATIC);
762: data_out.writeUTF("()V");
763: }
764:
765: Constructor constructor;
766: Constructor[] constructors = cl.getDeclaredConstructors();
767: Arrays.sort (constructors, memberComparator);
768: for (int i = 0; i < constructors.length; i++)
769: {
770: constructor = constructors[i];
771: modifiers = constructor.getModifiers();
772: if (Modifier.isPrivate(modifiers))
773: continue;
774:
775: data_out.writeUTF("<init>");
776: data_out.writeInt(modifiers);
777:
778:
779:
780: data_out.writeUTF
781: (TypeSignature.getEncodingOfConstructor(constructor).replace('/','.'));
782: }
783:
784: Method method;
785: Method[] methods = cl.getDeclaredMethods();
786: Arrays.sort(methods, memberComparator);
787: for (int i = 0; i < methods.length; i++)
788: {
789: method = methods[i];
790: modifiers = method.getModifiers();
791: if (Modifier.isPrivate(modifiers))
792: continue;
793:
794: data_out.writeUTF(method.getName());
795: data_out.writeInt(modifiers);
796:
797:
798:
799: data_out.writeUTF
800: (TypeSignature.getEncodingOfMethod(method).replace('/', '.'));
801: }
802:
803: data_out.close();
804: byte[] sha = md.digest();
805: long result = 0;
806: int len = sha.length < 8 ? sha.length : 8;
807: for (int i = 0; i < len; i++)
808: result += (long) (sha[i] & 0xFF) << (8 * i);
809:
810: return result;
811: }
812: catch (NoSuchAlgorithmException e)
813: {
814: throw new RuntimeException
815: ("The SHA algorithm was not found to use in computing the Serial Version UID for class "
816: + cl.getName(), e);
817: }
818: catch (IOException ioe)
819: {
820: throw new RuntimeException(ioe);
821: }
822: }
823:
824:
833: private ObjectStreamField[] getSerialPersistentFields(Class clazz)
834: throws NoSuchFieldException, IllegalAccessException
835: {
836: ObjectStreamField[] fieldsArray = null;
837: ObjectStreamField[] o;
838:
839:
840:
841: Field f = clazz.getDeclaredField("serialPersistentFields");
842: f.setAccessible(true);
843:
844: int modifiers = f.getModifiers();
845: if (!(Modifier.isStatic(modifiers) &&
846: Modifier.isFinal(modifiers) &&
847: Modifier.isPrivate(modifiers)))
848: return null;
849:
850: o = (ObjectStreamField[]) f.get(null);
851:
852: if (o == null)
853: return null;
854:
855: fieldsArray = new ObjectStreamField[ o.length ];
856: System.arraycopy(o, 0, fieldsArray, 0, o.length);
857:
858: return fieldsArray;
859: }
860:
861:
868: Externalizable newInstance() throws InvalidClassException
869: {
870: synchronized(this)
871: {
872: if (constructor == null)
873: {
874: try
875: {
876: final Constructor c = clazz.getConstructor(new Class[0]);
877:
878: AccessController.doPrivileged(new PrivilegedAction()
879: {
880: public Object run()
881: {
882: c.setAccessible(true);
883: return null;
884: }
885: });
886:
887: constructor = c;
888: }
889: catch(NoSuchMethodException x)
890: {
891: throw new InvalidClassException(clazz.getName(),
892: "No public zero-argument constructor");
893: }
894: }
895: }
896:
897: try
898: {
899: return (Externalizable)constructor.newInstance(null);
900: }
901: catch(Exception x)
902: {
903: throw (InvalidClassException)
904: new InvalidClassException(clazz.getName(),
905: "Unable to instantiate").initCause(x);
906: }
907: }
908:
909: public static final ObjectStreamField[] NO_FIELDS = {};
910:
911: private static Hashtable classLookupTable = new Hashtable();
912: private static final NullOutputStream nullOutputStream = new NullOutputStream();
913: private static final Comparator interfaceComparator = new InterfaceComparator();
914: private static final Comparator memberComparator = new MemberComparator();
915: private static final
916: Class[] writeMethodArgTypes = { java.io.ObjectOutputStream.class };
917:
918: private ObjectStreamClass superClass;
919: private Class clazz;
920: private String name;
921: private long uid;
922: private byte flags;
923:
924:
925:
926: ObjectStreamField[] fields;
927:
928:
929: int primFieldSize = -1;
930: int objectFieldCount;
931:
932: Method readObjectMethod;
933: Method readResolveMethod;
934: Method writeReplaceMethod;
935: Method writeObjectMethod;
936: boolean realClassIsSerializable;
937: boolean realClassIsExternalizable;
938: ObjectStreamField[] fieldMapping;
939: Constructor firstNonSerializableParentConstructor;
940: private Constructor constructor;
941:
942: boolean isProxyClass = false;
943:
944:
945:
946: private static final long serialVersionUID = -6120832682080437368L;
947:
948:
949:
950: private static final class InterfaceComparator implements Comparator
951: {
952: public int compare(Object o1, Object o2)
953: {
954: return ((Class) o1).getName().compareTo(((Class) o2).getName());
955: }
956: }
957:
958:
959:
960:
961: private static final class MemberComparator implements Comparator
962: {
963: public int compare(Object o1, Object o2)
964: {
965: Member m1 = (Member) o1;
966: Member m2 = (Member) o2;
967:
968: int comp = m1.getName().compareTo(m2.getName());
969:
970: if (comp == 0)
971: return TypeSignature.getEncodingOfMember(m1).
972: compareTo(TypeSignature.getEncodingOfMember(m2));
973: else
974: return comp;
975: }
976: }
977: }