1. /*
  2. * @(#)MarshalledObject.java 1.25 01/11/29
  3. *
  4. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.rmi;
  8. import java.io.*;
  9. import sun.rmi.server.MarshalInputStream;
  10. import sun.rmi.server.MarshalOutputStream;
  11. /**
  12. * A <code>MarshalledObject</code> contains a byte stream with the serialized
  13. * representation of an object given to its constructor. The <code>get</code>
  14. * method returns a new copy of the original object, as deserialized from
  15. * the contained byte stream. The contained object is serialized and
  16. * deserialized with the same serialization semantics used for marshaling
  17. * and unmarshaling parameters and return values of RMI calls: When the
  18. * serialized form is created:
  19. *
  20. * <ul>
  21. * <li> classes are annotated with a codebase URL from where the class
  22. * can be loaded (if available), and
  23. * <li> any remote object in the <code>MarshalledObject</code> is
  24. * represented by a serialized instance of its stub.
  25. * </ul>
  26. *
  27. * <p>When copy of the object is retrieved (via the <code>get</code> method),
  28. * if the class is not available locally, it will be loaded from the
  29. * appropriate location (specified the URL annotated with the class descriptor
  30. * when the class was serialized.
  31. *
  32. * <p><code>MarshalledObject</code> facilitates passing objects in RMI calls
  33. * that are not automatically deserialized immediately by the remote peer.
  34. *
  35. * @version 1.25, 11/29/01
  36. * @author Ann Wollrath
  37. * @author Peter Jones
  38. * @since JDK1.2
  39. */
  40. public final class MarshalledObject implements Serializable {
  41. /**
  42. * @serial Bytes of serialized representation. If <code>objBytes</code> is
  43. * <code>null</code> then the object marshalled was a <code>null</code>
  44. * reference.
  45. */
  46. private byte[] objBytes = null;
  47. /**
  48. * @serial Bytes of location annotations, which are ignored by
  49. * <code>equals</code>. If <code>locBytes</code> is null, there were no
  50. * non-<code>null</code> annotations during marshalling.
  51. */
  52. private byte[] locBytes = null;
  53. /**
  54. * @serial Stored hash code of contained object.
  55. *
  56. * @see #hashCode
  57. */
  58. private int hash;
  59. /** Indicate compatibility with JDK 1.2 version of class. */
  60. private static final long serialVersionUID = 8988374069173025854L;
  61. /**
  62. * Creates a new <code>MarshalledObject</code> that contains the
  63. * serialized representation of the current state of the supplied object.
  64. * The object is serialized with the semantics used for marshaling
  65. * parameters for RMI calls.
  66. *
  67. * @param obj the object to be serialized (must be serializable)
  68. * @exception IOException if an <code>IOException</code> occurs; an
  69. * <code>IOException</code> may occur if <code>obj</code> is not
  70. * serializable.
  71. * @since JDK1.2
  72. */
  73. public MarshalledObject(Object obj)
  74. throws java.io.IOException
  75. {
  76. if (obj == null) {
  77. hash = 13;
  78. return;
  79. }
  80. ByteArrayOutputStream bout = new ByteArrayOutputStream();
  81. ByteArrayOutputStream lout = new ByteArrayOutputStream();
  82. MarshalledObjectOutputStream out =
  83. new MarshalledObjectOutputStream(bout, lout);
  84. out.writeObject(obj);
  85. out.flush();
  86. objBytes = bout.toByteArray();
  87. // locBytes is null if no annotations
  88. locBytes = (out.hadAnnotations() ? lout.toByteArray() : null);
  89. /*
  90. * Calculate hash from the marshalled representation of object
  91. * so the hashcode will be comparable when sent between VMs.
  92. */
  93. int h = 0;
  94. for (int i = 0; i < objBytes.length; i++) {
  95. h = 31 * h + objBytes[i];
  96. }
  97. hash = h;
  98. }
  99. /**
  100. * Returns a new copy of the contained marshalledobject. The internal
  101. * representation is deserialized with the semantics used for
  102. * unmarshaling paramters for RMI calls.
  103. *
  104. * @return a copy of the contained object
  105. * @exception IOException if an <code>IOException</code> occurs while
  106. * deserializing the object from its internal representation.
  107. * @exception ClassNotFoundException if a
  108. * <code>ClassNotFoundException</code> occurs while deserializing the
  109. * object from its internal representation.
  110. * could not be found
  111. * @since JDK1.2
  112. */
  113. public Object get()
  114. throws java.io.IOException, java.lang.ClassNotFoundException
  115. {
  116. if (objBytes == null) // must have been a null object
  117. return null;
  118. ByteArrayInputStream bin = new ByteArrayInputStream(objBytes);
  119. // locBytes is null if no annotations
  120. ByteArrayInputStream lin =
  121. (locBytes == null ? null : new ByteArrayInputStream(locBytes));
  122. MarshalledObjectInputStream in =
  123. new MarshalledObjectInputStream(bin, lin);
  124. return in.readObject();
  125. }
  126. /**
  127. * Return a hash code for this <code>MarshalledObject</code>.
  128. *
  129. * @return a hash code
  130. */
  131. public int hashCode() {
  132. return hash;
  133. }
  134. /**
  135. * Compares this <code>MarshalledObject</code> to another object.
  136. * Returns true if and only if the argument refers to a
  137. * <code>MarshalledObject</code> that contains exactly the same
  138. * serialized representation of an object as this one does. The
  139. * comparison ignores any class codebase annotation, meaning that
  140. * two objects are equivalent if they have the same serialized
  141. * representation <i>except</i> for the codebase of each class
  142. * in the serialized representation.
  143. *
  144. * @param obj the object to compare with this <code>MarshalledObject</code>
  145. * @return <code>true</code> if the argument contains an equaivalent
  146. * serialized object; <code>false</code> otherwise
  147. * @since JDK1.2
  148. */
  149. public boolean equals(Object obj) {
  150. if (obj == this)
  151. return true;
  152. if (obj != null && obj instanceof MarshalledObject) {
  153. MarshalledObject other = (MarshalledObject) obj;
  154. // if either is a ref to null, both must be
  155. if (objBytes == null || other.objBytes == null)
  156. return objBytes == other.objBytes;
  157. // quick, easy test
  158. if (objBytes.length != other.objBytes.length)
  159. return false;
  160. //!! There is talk about adding an array comparision method
  161. //!! at JDK1.2 -- if so, this should be rewritten. -arnold
  162. for (int i = 0; i < objBytes.length; ++i) {
  163. if (objBytes[i] != other.objBytes[i])
  164. return false;
  165. }
  166. return true;
  167. } else {
  168. return false;
  169. }
  170. }
  171. /**
  172. * This class is used to marshal objects for
  173. * <code>MarshalledObject</code>. It places the location annotations
  174. * to one side so that two <code>MarshalledObject</code>s can be
  175. * compared for equality if they differ only in location
  176. * annotations. Objects written using this stream should be read back
  177. * from a <code>MarshalledObjectInputStream</code>.
  178. *
  179. * @see java.rmi.MarshalledObject
  180. * @see MarshalledObjectInputStream
  181. */
  182. private static class MarshalledObjectOutputStream
  183. extends MarshalOutputStream
  184. {
  185. /** The stream on which location objects are written. */
  186. private ObjectOutputStream locOut;
  187. /** <code>true</code> if non-<code>null</code> annotations are
  188. * written.
  189. */
  190. private boolean hadAnnotations;
  191. /**
  192. * Creates a new <code>MarshalledObjectOutputStream</code> whose
  193. * non-location bytes will be written to <code>objOut</code> and whose
  194. * location annotations (if any) will be written to
  195. * <code>locOut</code>.
  196. */
  197. public MarshalledObjectOutputStream(OutputStream objOut,
  198. OutputStream locOut)
  199. throws IOException
  200. {
  201. super(objOut);
  202. this.useProtocolVersion(ObjectStreamConstants.PROTOCOL_VERSION_2);
  203. this.locOut = new ObjectOutputStream(locOut);
  204. hadAnnotations = false;
  205. }
  206. /**
  207. * Returns <code>true</code> if any non-<code>null</code> location
  208. * annotations have been written to this stream.
  209. */
  210. public boolean hadAnnotations() {
  211. return hadAnnotations;
  212. }
  213. /**
  214. * Overrides MarshalOutputStream.writeLocation implementation to write
  215. * annotations to the location stream.
  216. */
  217. protected void writeLocation(String loc) throws IOException {
  218. hadAnnotations |= (loc != null);
  219. locOut.writeObject(loc);
  220. }
  221. public void flush() throws IOException {
  222. super.flush();
  223. locOut.flush();
  224. }
  225. }
  226. /**
  227. * The counterpart to <code>MarshalledObjectOutputStream</code>.
  228. *
  229. * @see MarshalledObjectOutputStream
  230. */
  231. private static class MarshalledObjectInputStream
  232. extends MarshalInputStream
  233. {
  234. /**
  235. * The stream from which annotations will be read. If this is
  236. * <code>null</code>, then all annotations were <code>null</code>.
  237. */
  238. private ObjectInputStream locIn;
  239. /**
  240. * Creates a new <code>MarshalledObjectInputStream</code> that
  241. * reads its objects from <code>objIn</code> and annotations
  242. * from <code>locIn</code>. If <code>locIn</code> is
  243. * <code>null</code>, then all annotations will be
  244. * <code>null</code>.
  245. */
  246. MarshalledObjectInputStream(InputStream objIn, InputStream locIn)
  247. throws IOException
  248. {
  249. super(objIn);
  250. this.locIn = (locIn == null ? null : new ObjectInputStream(locIn));
  251. }
  252. /**
  253. * Overrides MarshalInputStream.readLocation to return locations from
  254. * the stream we were given, or <code>null</code> if we were given a
  255. * <code>null</code> location stream.
  256. */
  257. protected Object readLocation()
  258. throws IOException, ClassNotFoundException
  259. {
  260. return (locIn == null ? null : locIn.readObject());
  261. }
  262. }
  263. }