1. /*
  2. * @(#)ObjectStreamClass.java 1.82 02/01/25
  3. *
  4. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.io;
  8. import java.security.MessageDigest;
  9. import java.security.NoSuchAlgorithmException;
  10. import java.security.DigestOutputStream;
  11. import java.security.AccessController;
  12. import java.security.PrivilegedAction;
  13. import java.lang.reflect.AccessibleObject;
  14. import java.lang.reflect.Modifier;
  15. import java.lang.reflect.Field;
  16. import java.lang.reflect.Member;
  17. import java.lang.reflect.Method;
  18. import java.lang.reflect.Constructor;
  19. import java.util.Arrays;
  20. import java.util.Comparator;
  21. import java.util.Iterator;
  22. import sun.misc.SoftCache;
  23. /**
  24. * Serialization's descriptor for classes.
  25. * It contains the name and serialVersionUID of the class.
  26. * <br>
  27. * The ObjectStreamClass for a specific class loaded in this Java VM can
  28. * be found/created using the lookup method.<p>
  29. * The algorithm to compute the SerialVersionUID is described in
  30. * <a href="http://java.sun.com/products/jdk/1.2/docs/guide/serialization/spec/class.doc4.html"> Object Serialization Specification, Section 4.4, Stream Unique Identifiers</a>.
  31. *
  32. * @author Roger Riggs
  33. * @version 1.82 01/25/02
  34. * @see ObjectStreamField
  35. * @see <a href="http://java.sun.com/products/jdk/1.2/docs/guide/serialization/spec/class.doc.html"> Object Serialization Specification, Section 4, Class Descriptors</a>
  36. * @since JDK1.1
  37. */
  38. public class ObjectStreamClass implements java.io.Serializable {
  39. /**
  40. * Find the descriptor for a class that can be serialized.
  41. * Creates an ObjectStreamClass instance if one does not exist
  42. * yet for class. Null is returned if the specified class does not
  43. * implement java.io.Serializable or java.io.Externalizable.
  44. */
  45. public static ObjectStreamClass lookup(Class cl)
  46. {
  47. ObjectStreamClass desc = lookupInternal(cl);
  48. if (desc.isSerializable() || desc.isExternalizable())
  49. return desc;
  50. return null;
  51. }
  52. /*
  53. * Find the class descriptor for the specified class.
  54. * Package access only so it can be called from ObjectIn/OutStream.
  55. */
  56. static ObjectStreamClass lookupInternal(Class cl)
  57. {
  58. /*
  59. * Note: using the class directly as the key for storing entries does
  60. * not pin the class indefinitely, since SoftCache removes strong refs
  61. * to keys when the corresponding values are gc'ed.
  62. */
  63. Object entry;
  64. EntryFuture future = null;
  65. synchronized (localDescs) {
  66. if ((entry = localDescs.get(cl)) == null) {
  67. localDescs.put(cl, future = new EntryFuture());
  68. }
  69. }
  70. if (entry instanceof ObjectStreamClass) { // check common case first
  71. return (ObjectStreamClass) entry;
  72. } else if (entry instanceof EntryFuture) {
  73. entry = ((EntryFuture) entry).get();
  74. } else if (entry == null) {
  75. try {
  76. entry = createLocalDescriptor(cl);
  77. } catch (Throwable th) {
  78. entry = th;
  79. }
  80. future.set(entry);
  81. synchronized (localDescs) {
  82. localDescs.put(cl, entry);
  83. }
  84. }
  85. if (entry instanceof ObjectStreamClass) {
  86. return (ObjectStreamClass) entry;
  87. } else if (entry instanceof RuntimeException) {
  88. throw (RuntimeException) entry;
  89. } else if (entry instanceof Error) {
  90. throw (Error) entry;
  91. } else {
  92. throw new InternalError("unexpected entry: " + entry);
  93. }
  94. }
  95. /*
  96. * Creates local class descriptor for the given class.
  97. */
  98. private static ObjectStreamClass createLocalDescriptor(Class cl) {
  99. /* Check if it's serializable */
  100. boolean serializable = Serializable.class.isAssignableFrom(cl);
  101. /* If the class is only Serializable,
  102. * lookup the descriptor for the superclass.
  103. */
  104. ObjectStreamClass superdesc = null;
  105. if (serializable) {
  106. Class superclass = cl.getSuperclass();
  107. if (superclass != null)
  108. superdesc = lookup(superclass);
  109. }
  110. /* Check if its' externalizable.
  111. * If it's Externalizable, clear the serializable flag.
  112. * Only one or the other may be set in the protocol.
  113. */
  114. boolean externalizable = false;
  115. if (serializable) {
  116. externalizable =
  117. ((superdesc != null) && superdesc.isExternalizable()) ||
  118. Externalizable.class.isAssignableFrom(cl);
  119. if (externalizable) {
  120. serializable = false;
  121. }
  122. }
  123. /* Create a new version descriptor,
  124. */
  125. return new ObjectStreamClass(cl, superdesc,
  126. serializable, externalizable);
  127. }
  128. /**
  129. * The name of the class described by this descriptor.
  130. */
  131. public String getName() {
  132. return name;
  133. }
  134. /**
  135. * Return the serialVersionUID for this class.
  136. * The serialVersionUID defines a set of classes all with the same name
  137. * that have evolved from a common root class and agree to be serialized
  138. * and deserialized using a common format.
  139. * NonSerializable classes have a serialVersionUID of 0L.
  140. */
  141. public long getSerialVersionUID() {
  142. return suid;
  143. }
  144. /**
  145. * Return the class in the local VM that this version is mapped to.
  146. * Null is returned if there is no corresponding local class.
  147. */
  148. public Class forClass() {
  149. return ofClass;
  150. }
  151. /**
  152. * Return an array of the fields of this serializable class.
  153. * @return an array containing an element for each persistent
  154. * field of this class. Returns an array of length zero if
  155. * there are no fields.
  156. * @since JDK1.2
  157. */
  158. public ObjectStreamField[] getFields() {
  159. // Return a copy so the caller can't change the fields.
  160. if (fields.length > 0) {
  161. ObjectStreamField[] dup = new ObjectStreamField[fields.length];
  162. System.arraycopy(fields, 0, dup, 0, fields.length);
  163. return dup;
  164. } else {
  165. return fields;
  166. }
  167. }
  168. /* Avoid unnecessary allocations within package. */
  169. final ObjectStreamField[] getFieldsNoCopy() {
  170. return fields;
  171. }
  172. /**
  173. * Get the field of this class by name.
  174. * @return The ObjectStreamField object of the named field or null if there
  175. * is no such named field.
  176. */
  177. public ObjectStreamField getField(String name) {
  178. ObjectStreamField searchKey =
  179. ObjectStreamField.constructSearchKey(name, Byte.TYPE);
  180. int index = -1;
  181. if (objFields != fields.length) {
  182. // perform binary search over primitive fields.
  183. index = Arrays.binarySearch(fields, searchKey);
  184. }
  185. if (index < 0 && objFields > 0) {
  186. // perform binary search over object fields.
  187. searchKey.setSearchKeyTypeString(true);
  188. index = Arrays.binarySearch(fields, searchKey);
  189. }
  190. return (index < 0) ? null : fields[index];
  191. }
  192. /**
  193. * Get the field of this class by name and fieldType.
  194. * @return The ObjectStreamField object of the named field, type
  195. * or null if there is no such named field of fieldType.
  196. */
  197. ObjectStreamField getField(String name, Class fieldType) {
  198. ObjectStreamField searchKey =
  199. ObjectStreamField.constructSearchKey(name, fieldType);
  200. int index = Arrays.binarySearch(fields, searchKey);
  201. return (index < 0) ? null : fields[index];
  202. }
  203. /**
  204. * Return a string describing this ObjectStreamClass.
  205. */
  206. public String toString() {
  207. StringBuffer sb = new StringBuffer();
  208. sb.append(name);
  209. sb.append(": static final long serialVersionUID = ");
  210. sb.append(Long.toString(suid));
  211. sb.append("L;");
  212. return sb.toString();
  213. }
  214. /*
  215. * Create a new ObjectStreamClass from a loaded class.
  216. * Don't call this directly, call lookup instead.
  217. */
  218. private ObjectStreamClass(final Class cl, ObjectStreamClass superdesc,
  219. boolean serial, boolean extern)
  220. {
  221. ofClass = cl; /* created from this class */
  222. name = cl.getName();
  223. superclass = superdesc;
  224. serializable = serial;
  225. externalizable = extern;
  226. if (!serializable || externalizable) {
  227. fields = NO_FIELDS;
  228. } else if (serializable) {
  229. /* Ask for permission to override field access checks.
  230. */
  231. AccessController.doPrivileged(new PrivilegedAction() {
  232. public Object run() {
  233. /* Fill in the list of persistent fields.
  234. * If it is declared, use the declared serialPersistentFields.
  235. * Otherwise, extract the fields from the class itself.
  236. */
  237. try {
  238. Field pf = cl.getDeclaredField("serialPersistentFields");
  239. pf.setAccessible(true);
  240. ObjectStreamField[] f = (ObjectStreamField[])pf.get(cl);
  241. int mods = pf.getModifiers();
  242. //field must be private for security reasons.
  243. if (Modifier.isPrivate(mods)) {
  244. fields = f;
  245. }
  246. } catch (NoSuchFieldException e) {
  247. fields = null;
  248. } catch (IllegalAccessException e) {
  249. fields = null;
  250. } catch (IllegalArgumentException e) {
  251. fields = null;
  252. }
  253. if (fields == null) {
  254. /* Get all of the declared fields for this
  255. * Class. setAccessible on all fields so they
  256. * can be accessed later. Create a temporary
  257. * ObjectStreamField array to hold each
  258. * non-static, non-transient field. Then copy the
  259. * temporary array into an array of the correct
  260. * size once the number of fields is known.
  261. */
  262. Field[] actualfields = cl.getDeclaredFields();
  263. AccessibleObject.setAccessible(actualfields, true);
  264. int numFields = 0;
  265. ObjectStreamField[] tempFields =
  266. new ObjectStreamField[actualfields.length];
  267. for (int i = 0; i < actualfields.length; i++) {
  268. int modifiers = actualfields[i].getModifiers();
  269. if (!Modifier.isStatic(modifiers) &&
  270. !Modifier.isTransient(modifiers)) {
  271. tempFields[numFields++] =
  272. new ObjectStreamField(actualfields[i]);
  273. }
  274. }
  275. fields = new ObjectStreamField[numFields];
  276. System.arraycopy(tempFields, 0, fields, 0, numFields);
  277. } else {
  278. // For each declared persistent field, look for an actual
  279. // reflected Field. If there is one, make sure it's the correct
  280. // type and cache it in the ObjectStreamClass for that field.
  281. for (int j = fields.length-1; j >= 0; j--) {
  282. try {
  283. Field reflField = cl.getDeclaredField(fields[j].getName());
  284. if (fields[j].getType() == reflField.getType()) {
  285. reflField.setAccessible(true);
  286. fields[j].setField(reflField);
  287. } else {
  288. // TBD: Should this be flagged as an error?
  289. }
  290. } catch (NoSuchFieldException e) {
  291. // Nothing to do
  292. }
  293. }
  294. }
  295. return null;
  296. }
  297. });
  298. if (fields.length > 1)
  299. Arrays.sort(fields);
  300. /* Set up field data for use while writing using the API api. */
  301. computeFieldInfo();
  302. }
  303. /* Get the serialVersionUID from the class.
  304. * It uses the access override mechanism so make sure
  305. * the field objects is only used here.
  306. *
  307. * NonSerializable classes have a serialVerisonUID of 0L.
  308. */
  309. if (isNonSerializable()) {
  310. suid = 0L;
  311. } else {
  312. // Lookup special Serializable members using reflection.
  313. AccessController.doPrivileged(new PrivilegedAction() {
  314. public Object run() {
  315. try {
  316. Field f = cl.getDeclaredField("serialVersionUID");
  317. int mods = f.getModifiers();
  318. if (Modifier.isStatic(mods) && Modifier.isFinal(mods)) {
  319. f.setAccessible(true);
  320. suid = f.getLong(cl);
  321. } else {
  322. suid = computeSerialVersionUID(cl);
  323. }
  324. } catch (NoSuchFieldException ex) {
  325. suid = computeSerialVersionUID(cl);
  326. } catch (IllegalAccessException ex) {
  327. suid = computeSerialVersionUID(cl);
  328. }
  329. /* check for class provided substitution methods,
  330. * writeReplace and readResolve. Methods can not
  331. * be static.
  332. */
  333. writeReplaceMethod =
  334. getDeclaredMethod("writeReplace", NULL_ARGS,
  335. 0, Modifier.STATIC);
  336. if (writeReplaceMethod == null && superclass != null &&
  337. checkSuperMethodAccess(superclass.writeReplaceMethod)) {
  338. writeReplaceMethod = superclass.writeReplaceMethod;
  339. }
  340. readResolveMethod =
  341. getDeclaredMethod("readResolve", NULL_ARGS,
  342. 0, Modifier.STATIC);
  343. if (readResolveMethod == null && superclass != null &&
  344. checkSuperMethodAccess(superclass.readResolveMethod)) {
  345. readResolveMethod = superclass.readResolveMethod;
  346. }
  347. /* Cache lookup of writeObject and readObject for
  348. * Serializable classes. (Do not lookup for Externalizable)
  349. */
  350. if (serializable) {
  351. //Workaround compiler bug. See declaration for more detail.
  352. if (OOS_ARGS == null || OIS_ARGS == null) {
  353. initStaticMethodArgs();
  354. }
  355. //end Workaround.
  356. writeObjectMethod =
  357. getDeclaredMethod("writeObject", OOS_ARGS,
  358. Modifier.PRIVATE, Modifier.STATIC);
  359. if (writeObjectMethod != null) {
  360. hasWriteObjectMethod = true;
  361. }
  362. readObjectMethod =
  363. getDeclaredMethod("readObject", OIS_ARGS,
  364. Modifier.PRIVATE, Modifier.STATIC);
  365. }
  366. return null;
  367. }
  368. });
  369. }
  370. }
  371. /*
  372. * Create an empty ObjectStreamClass for a class about to be read.
  373. * This is separate from read so ObjectInputStream can assign the
  374. * wire handle early, before any nested ObjectStreamClass might
  375. * be read.
  376. */
  377. ObjectStreamClass(String n, long s) {
  378. name = n;
  379. suid = s;
  380. superclass = null;
  381. }
  382. /* Validate the compatibility of the stream class descriptor and
  383. * the specified local class.
  384. *
  385. * @exception InvalidClassException if stream and local class are
  386. * not compatible.
  387. */
  388. private void validateLocalClass(Class localCl) throws InvalidClassException {
  389. if (localClassDesc == null)
  390. throw new InvalidClassException(localCl.getName(),
  391. "Local class not compatible");
  392. if (suid != localClassDesc.suid) {
  393. /* Check for exceptional cases that allow mismatched suid. */
  394. /* Allow adding Serializable or Externalizable
  395. * to a later release of the class.
  396. */
  397. boolean addedSerialOrExtern =
  398. isNonSerializable() || localClassDesc.isNonSerializable();
  399. /* Disregard the suid of an array when name and localCl.Name differ.
  400. * If resolveClass() returns an array with a different package
  401. * name, the serialVersionUIDs will not match since the fully
  402. * qualified array class is used in the
  403. * computation of the array's serialVersionUID. There is
  404. * no way to set a permanent serialVersionUID for an array type.
  405. */
  406. boolean arraySUID = (localCl.isArray() && ! localCl.getName().equals(name));
  407. if (! arraySUID && ! addedSerialOrExtern ) {
  408. throw new InvalidClassException(localCl.getName(),
  409. "Local class not compatible:" +
  410. " stream classdesc serialVersionUID=" + suid +
  411. " local class serialVersionUID=" + localClassDesc.suid);
  412. }
  413. }
  414. /* compare the class names, stripping off package names. */
  415. if (! compareClassNames(name, localCl.getName(), '.'))
  416. throw new InvalidClassException(localCl.getName(),
  417. "Incompatible local class name. " +
  418. "Expected class name compatible with " +
  419. name);
  420. /*
  421. * Test that both implement either serializable or externalizable.
  422. */
  423. if ((serializable && localClassDesc.externalizable) ||
  424. (externalizable && localClassDesc.serializable))
  425. throw new InvalidClassException(localCl.getName(),
  426. "Serializable is incompatible with Externalizable");
  427. }
  428. /*
  429. * Set the local class that this stream class descriptor matches.
  430. * The base class name and serialization version id must match if
  431. * both classes are serializable.
  432. * Fill in the reflected Fields that will be used for reading.
  433. */
  434. void setClass(Class cl) throws InvalidClassException {
  435. /* Allow no local class implementation. Must be able to
  436. * skip objects in stream introduced by class evolution.
  437. */
  438. if (cl == null) {
  439. localClassDesc = null;
  440. ofClass = null;
  441. computeFieldInfo();
  442. return;
  443. }
  444. localClassDesc = lookupInternal(cl);
  445. validateLocalClass(cl);
  446. /* Disable instance deserialization when one class is serializable
  447. * and the other is not or if both the classes are neither serializable
  448. * nor externalizable.
  449. */
  450. if ((serializable != localClassDesc.serializable) ||
  451. (externalizable != localClassDesc.externalizable) ||
  452. (!serializable && !externalizable)) {
  453. /* Delay signaling InvalidClassException until trying
  454. * to deserialize an instance of this class. Allows
  455. * a previously nonSerialized class descriptor that was written
  456. * into the stream to be made Serializable
  457. * or Externalizable, in a later release.
  458. */
  459. disableInstanceDeserialization = true;
  460. ofClass = cl;
  461. return;
  462. }
  463. /* Set up the reflected Fields in the class where the value of each
  464. * field in this descriptor should be stored.
  465. * Each field in this ObjectStreamClass (the source) is located (by
  466. * name) in the ObjectStreamClass of the class(the destination).
  467. * In the usual (non-versioned case) the field is in both
  468. * descriptors and the types match, so the reflected Field is copied.
  469. * If the type does not match, a InvalidClass exception is thrown.
  470. * If the field is not present in the class, the reflected Field
  471. * remains null so the field will be read but discarded.
  472. * If extra fields are present in the class they are ignored. Their
  473. * values will be set to the default value by the object allocator.
  474. * Both the src and dest field list are sorted by type and name.
  475. */
  476. ObjectStreamField[] destfield =
  477. (ObjectStreamField[])localClassDesc.fields;
  478. ObjectStreamField[] srcfield =
  479. (ObjectStreamField[])fields;
  480. int j = 0;
  481. nextsrc:
  482. for (int i = 0; i < srcfield.length; i++ ) {
  483. /* Find this field in the dest*/
  484. for (int k = j; k < destfield.length; k++) {
  485. if (srcfield[i].getName().equals(destfield[k].getName())) {
  486. /* found match */
  487. if (srcfield[i].isPrimitive() &&
  488. !srcfield[i].typeEquals(destfield[k])) {
  489. throw new InvalidClassException(cl.getName(),
  490. "The type of field " +
  491. destfield[i].getName() +
  492. " of class " + name +
  493. " is incompatible.");
  494. }
  495. /* Skip over any fields in the dest that are not in the src */
  496. j = k;
  497. srcfield[i].setField(destfield[j].getField());
  498. // go on to the next source field
  499. continue nextsrc;
  500. }
  501. }
  502. }
  503. /* Set up field data for use while reading from the input stream. */
  504. computeFieldInfo();
  505. /* Remember the class this represents */
  506. ofClass = cl;
  507. /* get the cache of these methods from the local class
  508. * implementation.
  509. */
  510. readObjectMethod = localClassDesc.readObjectMethod;
  511. readResolveMethod = localClassDesc.readResolveMethod;
  512. }
  513. /* Compare the base class names of streamName and localName.
  514. *
  515. * @return Return true iff the base class name compare.
  516. * @parameter streamName Fully qualified class name.
  517. * @parameter localName Fully qualified class name.
  518. * @parameter pkgSeparator class names use either '.' or '/'.
  519. *
  520. * Only compare base class name to allow package renaming.
  521. */
  522. static boolean compareClassNames(String streamName,
  523. String localName,
  524. char pkgSeparator) {
  525. /* compare the class names, stripping off package names. */
  526. int streamNameIndex = streamName.lastIndexOf(pkgSeparator);
  527. if (streamNameIndex < 0)
  528. streamNameIndex = 0;
  529. int localNameIndex = localName.lastIndexOf(pkgSeparator);
  530. if (localNameIndex < 0)
  531. localNameIndex = 0;
  532. return streamName.regionMatches(false, streamNameIndex,
  533. localName, localNameIndex,
  534. streamName.length() - streamNameIndex);
  535. }
  536. /*
  537. * Compare the types of two class descriptors.
  538. * They match if they have the same class name and suid
  539. */
  540. boolean typeEquals(ObjectStreamClass other) {
  541. return (suid == other.suid) &&
  542. compareClassNames(name, other.name, '.');
  543. }
  544. /*
  545. * Return the superclass descriptor of this descriptor.
  546. */
  547. void setSuperclass(ObjectStreamClass s) {
  548. superclass = s;
  549. }
  550. /*
  551. * Return the superclass descriptor of this descriptor.
  552. */
  553. ObjectStreamClass getSuperclass() {
  554. return superclass;
  555. }
  556. /*
  557. * Return whether the class has a writeObject method
  558. */
  559. boolean hasWriteObject() {
  560. return hasWriteObjectMethod;
  561. }
  562. /*
  563. * Return true if all instances of 'this' Externalizable class
  564. * are written in block-data mode from the stream that 'this' was read
  565. * from. <p>
  566. *
  567. * In JDK 1.1, all Externalizable instances are not written
  568. * in block-data mode.
  569. * In JDK 1.2, all Externalizable instances, by default, are written
  570. * in block-data mode and the Externalizable instance is terminated with
  571. * tag TC_ENDBLOCKDATA. Change enabled the ability to skip Externalizable
  572. * instances.
  573. *
  574. * IMPLEMENTATION NOTE:
  575. * This should have been a mode maintained per stream; however,
  576. * for compatibility reasons, it was only possible to record
  577. * this change per class. All Externalizable classes within
  578. * a given stream should either have this mode enabled or
  579. * disabled. This is enforced by not allowing the PROTOCOL_VERSION
  580. * of a stream to he changed after any objects have been written.
  581. *
  582. * @see ObjectOuputStream#useProtocolVersion
  583. * @see ObjectStreamConstants#PROTOCOL_VERSION_1
  584. * @see ObjectStreamConstants#PROTOCOL_VERSION_2
  585. *
  586. * @since JDK 1.2
  587. */
  588. boolean hasExternalizableBlockDataMode() {
  589. return hasExternalizableBlockData;
  590. }
  591. /*
  592. * Return the ObjectStreamClass of the local class this one is based on.
  593. */
  594. ObjectStreamClass localClassDescriptor() {
  595. return localClassDesc;
  596. }
  597. /*
  598. * Get the Serializability of the class.
  599. */
  600. boolean isSerializable() {
  601. return serializable;
  602. }
  603. /*
  604. * Get the externalizability of the class.
  605. */
  606. boolean isExternalizable() {
  607. return externalizable;
  608. }
  609. boolean isNonSerializable() {
  610. return ! (externalizable || serializable);
  611. }
  612. /*
  613. * Calculate the size of the array needed to store primitive data and the
  614. * number of object references to read when reading from the input
  615. * stream.
  616. */
  617. private void computeFieldInfo() {
  618. primBytes = 0;
  619. objFields = 0;
  620. for (int i = 0; i < fields.length; i++ ) {
  621. switch (fields[i].getTypeCode()) {
  622. case 'B':
  623. case 'Z':
  624. fields[i].setOffset(primBytes);
  625. primBytes += 1;
  626. break;
  627. case 'C':
  628. case 'S':
  629. fields[i].setOffset(primBytes);
  630. primBytes += 2;
  631. break;
  632. case 'I':
  633. case 'F':
  634. fields[i].setOffset(primBytes);
  635. primBytes += 4;
  636. break;
  637. case 'J':
  638. case 'D' :
  639. fields[i].setOffset(primBytes);
  640. primBytes += 8;
  641. break;
  642. case 'L':
  643. case '[':
  644. fields[i].setOffset(objFields);
  645. objFields += 1;
  646. break;
  647. }
  648. }
  649. }
  650. /*
  651. * Compute a hash for the specified class. Incrementally add
  652. * items to the hash accumulating in the digest stream.
  653. * Fold the hash into a long. Use the SHA secure hash function.
  654. */
  655. private static long computeSerialVersionUID(Class cl) {
  656. ByteArrayOutputStream devnull = new ByteArrayOutputStream(512);
  657. long h = 0;
  658. try {
  659. MessageDigest md = MessageDigest.getInstance("SHA");
  660. DigestOutputStream mdo = new DigestOutputStream(devnull, md);
  661. DataOutputStream data = new DataOutputStream(mdo);
  662. data.writeUTF(cl.getName());
  663. int classaccess = cl.getModifiers();
  664. classaccess &= (Modifier.PUBLIC | Modifier.FINAL |
  665. Modifier.INTERFACE | Modifier.ABSTRACT);
  666. /* Workaround for javac bug that only set ABSTRACT for
  667. * interfaces if the interface had some methods.
  668. * The ABSTRACT bit reflects that the number of methods > 0.
  669. * This is required so correct hashes can be computed
  670. * for existing class files.
  671. * Previously this hack was previously present in the VM.
  672. */
  673. Method[] method = cl.getDeclaredMethods();
  674. if ((classaccess & Modifier.INTERFACE) != 0) {
  675. classaccess &= (~Modifier.ABSTRACT);
  676. if (method.length > 0) {
  677. classaccess |= Modifier.ABSTRACT;
  678. }
  679. }
  680. data.writeInt(classaccess);
  681. /*
  682. * Get the list of interfaces supported,
  683. * Accumulate their names in Lexical order
  684. * and add them to the hash
  685. */
  686. if (!cl.isArray()) {
  687. /* In JDK1.2fcs, getInterfaces() was modified to return
  688. * {java.lang.Cloneable, java.io.Serializable} when
  689. * called on array classes. These values would upset
  690. * the computation of the hash, so we explicitly omit
  691. * them from its computation.
  692. */
  693. Class interfaces[] = cl.getInterfaces();
  694. Arrays.sort(interfaces, compareClassByName);
  695. for (int i = 0; i < interfaces.length; i++) {
  696. data.writeUTF(interfaces[i].getName());
  697. }
  698. }
  699. /* Sort the field names to get a deterministic order */
  700. Field[] field = cl.getDeclaredFields();
  701. Arrays.sort(field, compareMemberByName);
  702. for (int i = 0; i < field.length; i++) {
  703. Field f = field[i];
  704. /* Include in the hash all fields except those that are
  705. * private transient and private static.
  706. */
  707. int m = f.getModifiers();
  708. if (Modifier.isPrivate(m) &&
  709. (Modifier.isTransient(m) || Modifier.isStatic(m)))
  710. continue;
  711. data.writeUTF(f.getName());
  712. data.writeInt(m);
  713. data.writeUTF(getSignature(f.getType()));
  714. }
  715. if (hasStaticInitializer(cl)) {
  716. data.writeUTF("<clinit>");
  717. data.writeInt(Modifier.STATIC); // TBD: what modifiers does it have
  718. data.writeUTF("()V");
  719. }
  720. /*
  721. * Get the list of constructors including name and signature
  722. * Sort lexically, add all except the private constructors
  723. * to the hash with their access flags
  724. */
  725. MethodSignature[] constructors =
  726. MethodSignature.removePrivateAndSort(cl.getDeclaredConstructors());
  727. for (int i = 0; i < constructors.length; i++) {
  728. MethodSignature c = constructors[i];
  729. String mname = "<init>";
  730. String desc = c.signature;
  731. desc = desc.replace('/', '.');
  732. data.writeUTF(mname);
  733. data.writeInt(c.member.getModifiers());
  734. data.writeUTF(desc);
  735. }
  736. /* Include in the hash all methods except those that are
  737. * private transient and private static.
  738. */
  739. MethodSignature[] methods =
  740. MethodSignature.removePrivateAndSort(method);
  741. for (int i = 0; i < methods.length; i++ ) {
  742. MethodSignature m = methods[i];
  743. String desc = m.signature;
  744. desc = desc.replace('/', '.');
  745. data.writeUTF(m.member.getName());
  746. data.writeInt(m.member.getModifiers());
  747. data.writeUTF(desc);
  748. }
  749. /* Compute the hash value for this class.
  750. * Use only the first 64 bits of the hash.
  751. */
  752. data.flush();
  753. byte hasharray[] = md.digest();
  754. for (int i = 0; i < Math.min(8, hasharray.length); i++) {
  755. h += (long)(hasharray[i] & 255) << (i * 8);
  756. }
  757. } catch (IOException ignore) {
  758. /* can't happen, but be deterministic anyway. */
  759. h = -1;
  760. } catch (NoSuchAlgorithmException complain) {
  761. throw new SecurityException(complain.getMessage());
  762. }
  763. return h;
  764. }
  765. /**
  766. * Compute the JVM signature for the class.
  767. */
  768. static String getSignature(Class clazz) {
  769. String type = null;
  770. if (clazz.isArray()) {
  771. Class cl = clazz;
  772. int dimensions = 0;
  773. while (cl.isArray()) {
  774. dimensions++;
  775. cl = cl.getComponentType();
  776. }
  777. StringBuffer sb = new StringBuffer();
  778. for (int i = 0; i < dimensions; i++) {
  779. sb.append("[");
  780. }
  781. sb.append(getSignature(cl));
  782. type = sb.toString();
  783. } else if (clazz.isPrimitive()) {
  784. if (clazz == Integer.TYPE) {
  785. type = "I";
  786. } else if (clazz == Byte.TYPE) {
  787. type = "B";
  788. } else if (clazz == Long.TYPE) {
  789. type = "J";
  790. } else if (clazz == Float.TYPE) {
  791. type = "F";
  792. } else if (clazz == Double.TYPE) {
  793. type = "D";
  794. } else if (clazz == Short.TYPE) {
  795. type = "S";
  796. } else if (clazz == Character.TYPE) {
  797. type = "C";
  798. } else if (clazz == Boolean.TYPE) {
  799. type = "Z";
  800. } else if (clazz == Void.TYPE) {
  801. type = "V";
  802. }
  803. } else {
  804. type = "L" + clazz.getName().replace('.', '/') + ";";
  805. }
  806. return type;
  807. }
  808. /*
  809. * Compute the JVM method descriptor for the method.
  810. */
  811. static String getSignature(Method meth) {
  812. StringBuffer sb = new StringBuffer();
  813. sb.append("(");
  814. Class[] params = meth.getParameterTypes(); // avoid clone
  815. for (int j = 0; j < params.length; j++) {
  816. sb.append(getSignature(params[j]));
  817. }
  818. sb.append(")");
  819. sb.append(getSignature(meth.getReturnType()));
  820. return sb.toString();
  821. }
  822. /*
  823. * Compute the JVM constructor descriptor for the constructor.
  824. */
  825. static String getSignature(Constructor cons) {
  826. StringBuffer sb = new StringBuffer();
  827. sb.append("(");
  828. Class[] params = cons.getParameterTypes(); // avoid clone
  829. for (int j = 0; j < params.length; j++) {
  830. sb.append(getSignature(params[j]));
  831. }
  832. sb.append(")V");
  833. return sb.toString();
  834. }
  835. /*
  836. * locate the ObjectStreamClass for this class and write it to the stream.
  837. *
  838. * @serialData
  839. * <code>primitive UTF-8 String</code> Qalified class name.
  840. * <code>long</code> Serial version unique identifier for compatible classes.
  841. * <code>byte</code>. Mask with <code>java.io.ObjectStreamConstants.SC_*</code>.<br>
  842. * <code>short</code>. Number of Serializable fields to follow. If 0, no more data.
  843. * <code>list of Serializable Field descriptors</code>. Descriptors for Primitive
  844. * typed fields are written first sorted by field name
  845. * followed by descriptors for the object typed fields sorted
  846. * by field name. The names are sorted using String.compareTo.
  847. *
  848. * A Serializable field consists of the following data:
  849. * <code>byte</code> TypeCode of field. See ObjectStreamField.getTypeCode().
  850. * <code>primitive UTF-8 encoded String</code> Unqualified name of field.
  851. * <code>String</code> Qualified class name.
  852. */
  853. void write(ObjectOutputStream s) throws IOException {
  854. /* write the flag indicating that this class has write/read object methods */
  855. int flags = 0;
  856. if (hasWriteObjectMethod)
  857. flags |= ObjectStreamConstants.SC_WRITE_METHOD;
  858. if (serializable)
  859. flags |= ObjectStreamConstants.SC_SERIALIZABLE;
  860. if (externalizable) {
  861. flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
  862. /* Enabling the SC_BLOCK_DATA flag indicates PROTCOL_VERSION_2.*/
  863. if (! s.useDeprecatedExternalizableFormat)
  864. flags |= ObjectStreamConstants.SC_BLOCK_DATA;
  865. }
  866. s.writeByte(flags);
  867. // If there are no fields, write a null and return
  868. if (fields == null) {
  869. s.writeShort(0);
  870. return;
  871. }
  872. /* write the total number of fields */
  873. s.writeShort(fields.length);
  874. /* Write out the descriptors of the primitive fields Each
  875. * descriptor consists of the UTF fieldname, a short for the
  876. * access modes, and the first byte of the signature byte.
  877. * For the object types, ('[' and 'L'), a reference to the
  878. * type of the field follows.
  879. */
  880. for (int i = 0; i < fields.length; i++ ) {
  881. ObjectStreamField f = fields[i];
  882. s.writeByte(f.getTypeCode());
  883. s.writeUTF(f.getName());
  884. if (!f.isPrimitive()) {
  885. s.writeTypeString(f.getTypeString());
  886. }
  887. }
  888. }
  889. /*
  890. * Read the version descriptor from the stream.
  891. * Write the count of field descriptors
  892. * for each descriptor write the first character of its type,
  893. * the name of the field.
  894. * If the type is for an object either array or object, write
  895. * the type typedescriptor for the type
  896. */
  897. void read(ObjectInputStream s) throws IOException, ClassNotFoundException {
  898. /* read flags and determine whether the source class had
  899. * write/read methods.
  900. */
  901. byte flags = s.readByte();
  902. serializable = (flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0;
  903. externalizable = (flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0;
  904. hasWriteObjectMethod = serializable ?
  905. (flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0 :
  906. false;
  907. hasExternalizableBlockData = externalizable ?
  908. (flags & ObjectStreamConstants.SC_BLOCK_DATA) != 0 :
  909. false;
  910. /* Read the number of fields described.
  911. * For each field read the type byte, the name.
  912. */
  913. int count = s.readShort();
  914. fields = new ObjectStreamField[count];
  915. /* disable replacement of String objects read
  916. * by ObjectStreamClass. */
  917. boolean prevEnableResolve = s.enableResolve;
  918. s.enableResolve = false;
  919. try {
  920. for (int i = 0; i < count; i++ ) {
  921. char type = (char)s.readByte();
  922. String name = s.readUTF();
  923. String ftype = null;
  924. if (type == '[' || type == 'L') {
  925. ftype = (String)s.readObject();
  926. }
  927. fields[i] =
  928. new ObjectStreamField(name, type, null, ftype);
  929. }
  930. } finally {
  931. s.enableResolve = prevEnableResolve;
  932. }
  933. }
  934. /* To accomodate nonSerializable classes written into a stream,
  935. * this check must be delayed until an instance is deserialized.
  936. */
  937. void verifyInstanceDeserialization() throws InvalidClassException {
  938. if (disableInstanceDeserialization) {
  939. String name = (serializable || externalizable) ?
  940. localClassDesc.getName() : getName();
  941. String stype = (serializable || localClassDesc.serializable) ?
  942. "Serializable" :
  943. (externalizable || localClassDesc.externalizable) ?
  944. "Externalizable" : "Serializable or Externalizable";
  945. throw new InvalidClassException(name, "is not " + stype);
  946. }
  947. }
  948. /**
  949. * Placeholder used in class descriptor lookup table for an entry in the
  950. * process of being initialized. (Internal) callers which receive an
  951. * EntryFuture as the result of a lookup should call the get() method of
  952. * the EntryFuture; this will return the actual entry once it is ready for
  953. * use and has been set(). To conserve objects, EntryFutures synchronize
  954. * on themselves.
  955. */
  956. private static class EntryFuture {
  957. private static final Object unset = new Object();
  958. private Object entry = unset;
  959. synchronized void set(Object entry) {
  960. if (this.entry != unset) {
  961. throw new IllegalStateException();
  962. }
  963. this.entry = entry;
  964. notifyAll();
  965. }
  966. synchronized Object get() {
  967. boolean interrupted = false;
  968. while (entry == unset) {
  969. try {
  970. wait();
  971. } catch (InterruptedException ex) {
  972. interrupted = true;
  973. }
  974. }
  975. if (interrupted) {
  976. AccessController.doPrivileged(
  977. new PrivilegedAction() {
  978. public Object run() {
  979. Thread.currentThread().interrupt();
  980. return null;
  981. }
  982. }
  983. );
  984. }
  985. return entry;
  986. }
  987. }
  988. /*
  989. * Cache of Class -> ClassDescriptor Mappings.
  990. */
  991. private static final SoftCache localDescs = new SoftCache(10);
  992. /*
  993. * The name of this descriptor
  994. */
  995. private String name;
  996. /*
  997. * The descriptor of the supertype.
  998. */
  999. private ObjectStreamClass superclass;
  1000. /*
  1001. * Flags for Serializable and Externalizable.
  1002. */
  1003. private boolean serializable;
  1004. private boolean externalizable;
  1005. /*
  1006. * Array of persistent fields of this class, sorted by
  1007. * type and name.
  1008. */
  1009. private ObjectStreamField[] fields;
  1010. /*
  1011. * Class that is a descriptor for in this virtual machine.
  1012. */
  1013. private Class ofClass;
  1014. /*
  1015. * SerialVersionUID for this class.
  1016. */
  1017. private long suid;
  1018. /*
  1019. * The total number of bytes of primitive fields.
  1020. * The total number of object fields.
  1021. */
  1022. int primBytes;
  1023. int objFields;
  1024. /* True if this class has/had a writeObject method */
  1025. private boolean hasWriteObjectMethod;
  1026. /* In JDK 1.1, external data was not written in block mode.
  1027. * As of JDK 1.2, external data is written in block data mode. This
  1028. * flag enables JDK 1.2 to be able to read JDK 1.1 written external data.
  1029. *
  1030. * @since JDK 1.2
  1031. */
  1032. private boolean hasExternalizableBlockData;
  1033. Method writeObjectMethod;
  1034. Method readObjectMethod;
  1035. Method readResolveMethod;
  1036. Method writeReplaceMethod;
  1037. /*
  1038. * ObjectStreamClass that this one was built from.
  1039. */
  1040. private ObjectStreamClass localClassDesc;
  1041. /* Indicates that stream and local class are not both
  1042. * serializable. No instances of this class can be deserialized.
  1043. */
  1044. private boolean disableInstanceDeserialization = false;
  1045. /* Find out if the class has a static class initializer <clinit> */
  1046. private static native boolean hasStaticInitializer(Class cl);
  1047. /** use serialVersionUID from JDK 1.1. for interoperability */
  1048. private static final long serialVersionUID = -6120832682080437368L;
  1049. /**
  1050. * Set serialPersistentFields of a Serializable class to this value to
  1051. * denote that the class has no Serializable fields.
  1052. */
  1053. public static final ObjectStreamField[] NO_FIELDS =
  1054. new ObjectStreamField[0];
  1055. /**
  1056. * Class ObjectStreamClass is special cased within the
  1057. * Serialization Stream Protocol.
  1058. *
  1059. * An ObjectStreamClass is written intially into an ObjectOutputStream
  1060. * in the following format:
  1061. * <pre>
  1062. * TC_CLASSDESC className, serialVersionUID, flags,
  1063. * length, list of field descriptions.
  1064. *
  1065. * FIELDNAME TYPES
  1066. * DESCRIPTION
  1067. * --------------------------------------
  1068. * className primitive data String
  1069. * Fully qualified class name.
  1070. *
  1071. * serialVersionUID long
  1072. * Stream Unique Identifier for compatible classes
  1073. * with same base class name.
  1074. *
  1075. * flags byte
  1076. * Attribute bit fields defined in
  1077. * <code>java.io.ObjectStreamConstants.SC_*</code>.
  1078. *
  1079. * length short
  1080. * The number of field descriptions to follow.
  1081. *
  1082. * fieldDescription (byte, primitive data String, String Object)
  1083. * A pseudo-externalized format of class
  1084. * <code>java.io.ObjectStreamField</code>.
  1085. * Consists of typeCode, fieldName, and,
  1086. * if a nonPrimitive typecode, a fully qualified
  1087. * class name. See <code>Class.getName</code> method
  1088. * for the typecode byte encodings.
  1089. * </pre>
  1090. * The first time the class descriptor
  1091. * is written into the stream, a new handle is generated.
  1092. * Future references to the class descriptor are
  1093. * written as references to the initial class descriptor instance.
  1094. *
  1095. * @see java.io.ObjectOutputStream#writeUTF(java.lang.String)
  1096. */
  1097. private static final ObjectStreamField[] serialPersistentFields =
  1098. NO_FIELDS;
  1099. /*
  1100. * Entries held in the Cache of known ObjectStreamClass objects.
  1101. * Entries are chained together with the same hash value (modulo array size).
  1102. */
  1103. private static class ObjectStreamClassEntry extends java.lang.ref.SoftReference
  1104. {
  1105. ObjectStreamClassEntry(ObjectStreamClass c) {
  1106. super(c);
  1107. }
  1108. ObjectStreamClassEntry next;
  1109. }
  1110. /*
  1111. * Comparator object for Classes and Interfaces
  1112. */
  1113. private static Comparator compareClassByName =
  1114. new CompareClassByName();
  1115. private static class CompareClassByName implements Comparator {
  1116. public int compare(Object o1, Object o2) {
  1117. Class c1 = (Class)o1;
  1118. Class c2 = (Class)o2;
  1119. return (c1.getName()).compareTo(c2.getName());
  1120. }
  1121. }
  1122. /*
  1123. * Comparator object for Members, Fields, and Methods
  1124. */
  1125. private static Comparator compareMemberByName =
  1126. new CompareMemberByName();
  1127. private static class CompareMemberByName implements Comparator {
  1128. public int compare(Object o1, Object o2) {
  1129. String s1 = ((Member)o1).getName();
  1130. String s2 = ((Member)o2).getName();
  1131. if (o1 instanceof Method) {
  1132. s1 += getSignature((Method)o1);
  1133. s2 += getSignature((Method)o2);
  1134. } else if (o1 instanceof Constructor) {
  1135. s1 += getSignature((Constructor)o1);
  1136. s2 += getSignature((Constructor)o2);
  1137. }
  1138. return s1.compareTo(s2);
  1139. }
  1140. }
  1141. /* It is expensive to recompute a method or constructor signature
  1142. many times, so compute it only once using this data structure. */
  1143. private static class MethodSignature implements Comparator {
  1144. Member member;
  1145. String signature; // cached parameter signature
  1146. /* Given an array of Method or Constructor members,
  1147. return a sorted array of the non-private members.*/
  1148. /* A better implementation would be to implement the returned data
  1149. structure as an insertion sorted link list.*/
  1150. static MethodSignature[] removePrivateAndSort(Member[] m) {
  1151. int numNonPrivate = 0;
  1152. for (int i = 0; i < m.length; i++) {
  1153. if (! Modifier.isPrivate(m[i].getModifiers())) {
  1154. numNonPrivate++;
  1155. }
  1156. }
  1157. MethodSignature[] cm = new MethodSignature[numNonPrivate];
  1158. int cmi = 0;
  1159. for (int i = 0; i < m.length; i++) {
  1160. if (! Modifier.isPrivate(m[i].getModifiers())) {
  1161. cm[cmi] = new MethodSignature(m[i]);
  1162. cmi++;
  1163. }
  1164. }
  1165. if (cmi > 0)
  1166. Arrays.sort(cm, cm[0]);
  1167. return cm;
  1168. }
  1169. /* Assumes that o1 and o2 are either both methods
  1170. or both constructors.*/
  1171. public int compare(Object o1, Object o2) {
  1172. /* Arrays.sort calls compare when o1 and o2 are equal.*/
  1173. if (o1 == o2)
  1174. return 0;
  1175. MethodSignature c1 = (MethodSignature)o1;
  1176. MethodSignature c2 = (MethodSignature)o2;
  1177. int result;
  1178. if (isConstructor()) {
  1179. result = c1.signature.compareTo(c2.signature);
  1180. } else { // is a Method.
  1181. result = c1.member.getName().compareTo(c2.member.getName());
  1182. if (result == 0)
  1183. result = c1.signature.compareTo(c2.signature);
  1184. }
  1185. return result;
  1186. }
  1187. private boolean isConstructor() {
  1188. return member instanceof Constructor;
  1189. }
  1190. private MethodSignature(Member m) {
  1191. member = m;
  1192. if (isConstructor()) {
  1193. signature = ObjectStreamClass.getSignature((Constructor)m);
  1194. } else {
  1195. signature = ObjectStreamClass.getSignature((Method)m);
  1196. }
  1197. }
  1198. }
  1199. boolean isResolvable() {
  1200. return readResolveMethod != null;
  1201. }
  1202. boolean isReplaceable() {
  1203. return writeReplaceMethod != null;
  1204. }
  1205. static Object invokeMethod(Method method, Object obj, Object[] args)
  1206. throws IOException
  1207. {
  1208. Object returnValue = null;
  1209. try {
  1210. returnValue = method.invoke(obj, args);
  1211. } catch (java.lang.reflect.InvocationTargetException e) {
  1212. Throwable t = e.getTargetException();
  1213. if (t instanceof IOException)
  1214. throw (IOException)t;
  1215. else if (t instanceof RuntimeException)
  1216. throw (RuntimeException) t;
  1217. else if (t instanceof Error)
  1218. throw (Error) t;
  1219. else
  1220. throw new Error("interal error");
  1221. } catch (IllegalAccessException e) {
  1222. // cannot happen
  1223. throw new Error("interal error");
  1224. }
  1225. return returnValue;
  1226. }
  1227. /* ASSUMPTION: Called within priviledged access block.
  1228. * Needed to set declared methods and to set the
  1229. * accessibility bit.
  1230. */
  1231. private Method getDeclaredMethod(String methodName, Class[] args,
  1232. int requiredModifierMask,
  1233. int disallowedModifierMask) {
  1234. Method method = null;
  1235. try {
  1236. method =
  1237. ofClass.getDeclaredMethod(methodName, args);
  1238. if (method != null) {
  1239. int mods = method.getModifiers();
  1240. if ((mods & disallowedModifierMask) != 0 ||
  1241. (mods & requiredModifierMask) != requiredModifierMask) {
  1242. method = null;
  1243. } else {
  1244. method.setAccessible(true);
  1245. }
  1246. }
  1247. } catch (NoSuchMethodException e) {
  1248. // Since it is alright if methodName does not exist,
  1249. // no need to do anything special here.
  1250. }
  1251. return method;
  1252. }
  1253. /*
  1254. * Return true if scMethod is accessible from the context of
  1255. * this ObjectStreamClass' local implementation class.
  1256. * Simulate java accessibility rules of accessing method 'scMethod'
  1257. * from a method within subclass this.forClass.
  1258. * If method would not be accessible, returns null.
  1259. *
  1260. * @param scMethod A method from the superclass of this ObjectStreamClass.
  1261. */
  1262. private boolean checkSuperMethodAccess(Method scMethod) {
  1263. if (scMethod == null) {
  1264. return false;
  1265. }
  1266. int supermods = scMethod.getModifiers();
  1267. if (Modifier.isPublic(supermods) || Modifier.isProtected(supermods)) {
  1268. return true;
  1269. } else if (Modifier.isPrivate(supermods)) {
  1270. return false;
  1271. } else {
  1272. // check package-private access.
  1273. return isSameClassPackage(scMethod.getDeclaringClass(), ofClass);
  1274. }
  1275. }
  1276. /* Will not work for array classes. */
  1277. static private boolean isSameClassPackage(Class cl1, Class cl2) {
  1278. if (cl1.getClassLoader() != cl2.getClassLoader()) {
  1279. return false;
  1280. } else {
  1281. String clName1 = cl1.getName();
  1282. String clName2 = cl2.getName();
  1283. int idx1 = clName1.lastIndexOf('.');
  1284. int idx2 = clName2.lastIndexOf('.');
  1285. if (idx1 == -1 || idx2 == -1) {
  1286. /* One of the two doesn't have a package. Only return true
  1287. * if the other one also does not have a package.
  1288. */
  1289. return idx1 == idx2;
  1290. } else {
  1291. return clName1.regionMatches(false, 0,
  1292. clName2, 0, idx1 - 1);
  1293. }
  1294. }
  1295. }
  1296. private final static Class[] NULL_ARGS = {};
  1297. //WORKAROUND compiler bug with following code.
  1298. //static final Class[] OIS_ARGS = {ObjectInpuStream.class};
  1299. //static final Class[] OOS_ARGS = {ObjectOutpuStream.class};
  1300. private static Class[] OIS_ARGS = null;
  1301. private static Class[] OOS_ARGS = null;
  1302. private static void initStaticMethodArgs() {
  1303. OOS_ARGS = new Class[1];
  1304. OOS_ARGS[0] = ObjectOutputStream.class;
  1305. OIS_ARGS = new Class[1];
  1306. OIS_ARGS[0] = ObjectInputStream.class;
  1307. }
  1308. }