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