blob: e10e4d651bfa03f862f1197d72eda002a863252f [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package java.rmi;
27
28import java.io.ByteArrayInputStream;
29import java.io.ByteArrayOutputStream;
30import java.io.IOException;
31import java.io.InputStream;
32import java.io.ObjectInputStream;
33import java.io.ObjectOutputStream;
34import java.io.ObjectStreamConstants;
35import java.io.OutputStream;
36import java.io.Serializable;
37import sun.rmi.server.MarshalInputStream;
38import sun.rmi.server.MarshalOutputStream;
39
40/**
41 * A <code>MarshalledObject</code> contains a byte stream with the serialized
42 * representation of an object given to its constructor. The <code>get</code>
43 * method returns a new copy of the original object, as deserialized from
44 * the contained byte stream. The contained object is serialized and
45 * deserialized with the same serialization semantics used for marshaling
46 * and unmarshaling parameters and return values of RMI calls: When the
47 * serialized form is created:
48 *
49 * <ul>
50 * <li> classes are annotated with a codebase URL from where the class
51 * can be loaded (if available), and
52 * <li> any remote object in the <code>MarshalledObject</code> is
53 * represented by a serialized instance of its stub.
54 * </ul>
55 *
56 * <p>When copy of the object is retrieved (via the <code>get</code> method),
57 * if the class is not available locally, it will be loaded from the
58 * appropriate location (specified the URL annotated with the class descriptor
59 * when the class was serialized.
60 *
61 * <p><code>MarshalledObject</code> facilitates passing objects in RMI calls
62 * that are not automatically deserialized immediately by the remote peer.
63 *
64 * @param <T> the type of the object contained in this
65 * <code>MarshalledObject</code>
66 *
67 * @author Ann Wollrath
68 * @author Peter Jones
69 * @since 1.2
70 */
71public final class MarshalledObject<T> implements Serializable {
72 /**
73 * @serial Bytes of serialized representation. If <code>objBytes</code> is
74 * <code>null</code> then the object marshalled was a <code>null</code>
75 * reference.
76 */
77 private byte[] objBytes = null;
78
79 /**
80 * @serial Bytes of location annotations, which are ignored by
81 * <code>equals</code>. If <code>locBytes</code> is null, there were no
82 * non-<code>null</code> annotations during marshalling.
83 */
84 private byte[] locBytes = null;
85
86 /**
87 * @serial Stored hash code of contained object.
88 *
89 * @see #hashCode
90 */
91 private int hash;
92
93 /** Indicate compatibility with 1.2 version of class. */
94 private static final long serialVersionUID = 8988374069173025854L;
95
96 /**
97 * Creates a new <code>MarshalledObject</code> that contains the
98 * serialized representation of the current state of the supplied object.
99 * The object is serialized with the semantics used for marshaling
100 * parameters for RMI calls.
101 *
102 * @param obj the object to be serialized (must be serializable)
103 * @exception IOException if an <code>IOException</code> occurs; an
104 * <code>IOException</code> may occur if <code>obj</code> is not
105 * serializable.
106 * @since 1.2
107 */
108 public MarshalledObject(T obj) throws IOException {
109 if (obj == null) {
110 hash = 13;
111 return;
112 }
113
114 ByteArrayOutputStream bout = new ByteArrayOutputStream();
115 ByteArrayOutputStream lout = new ByteArrayOutputStream();
116 MarshalledObjectOutputStream out =
117 new MarshalledObjectOutputStream(bout, lout);
118 out.writeObject(obj);
119 out.flush();
120 objBytes = bout.toByteArray();
121 // locBytes is null if no annotations
122 locBytes = (out.hadAnnotations() ? lout.toByteArray() : null);
123
124 /*
125 * Calculate hash from the marshalled representation of object
126 * so the hashcode will be comparable when sent between VMs.
127 */
128 int h = 0;
129 for (int i = 0; i < objBytes.length; i++) {
130 h = 31 * h + objBytes[i];
131 }
132 hash = h;
133 }
134
135 /**
136 * Returns a new copy of the contained marshalledobject. The internal
137 * representation is deserialized with the semantics used for
138 * unmarshaling paramters for RMI calls.
139 *
140 * @return a copy of the contained object
141 * @exception IOException if an <code>IOException</code> occurs while
142 * deserializing the object from its internal representation.
143 * @exception ClassNotFoundException if a
144 * <code>ClassNotFoundException</code> occurs while deserializing the
145 * object from its internal representation.
146 * could not be found
147 * @since 1.2
148 */
149 public T get() throws IOException, ClassNotFoundException {
150 if (objBytes == null) // must have been a null object
151 return null;
152
153 ByteArrayInputStream bin = new ByteArrayInputStream(objBytes);
154 // locBytes is null if no annotations
155 ByteArrayInputStream lin =
156 (locBytes == null ? null : new ByteArrayInputStream(locBytes));
157 MarshalledObjectInputStream in =
158 new MarshalledObjectInputStream(bin, lin);
159 T obj = (T) in.readObject();
160 in.close();
161 return obj;
162 }
163
164 /**
165 * Return a hash code for this <code>MarshalledObject</code>.
166 *
167 * @return a hash code
168 */
169 public int hashCode() {
170 return hash;
171 }
172
173 /**
174 * Compares this <code>MarshalledObject</code> to another object.
175 * Returns true if and only if the argument refers to a
176 * <code>MarshalledObject</code> that contains exactly the same
177 * serialized representation of an object as this one does. The
178 * comparison ignores any class codebase annotation, meaning that
179 * two objects are equivalent if they have the same serialized
180 * representation <i>except</i> for the codebase of each class
181 * in the serialized representation.
182 *
183 * @param obj the object to compare with this <code>MarshalledObject</code>
184 * @return <code>true</code> if the argument contains an equaivalent
185 * serialized object; <code>false</code> otherwise
186 * @since 1.2
187 */
188 public boolean equals(Object obj) {
189 if (obj == this)
190 return true;
191
192 if (obj != null && obj instanceof MarshalledObject) {
193 MarshalledObject other = (MarshalledObject) obj;
194
195 // if either is a ref to null, both must be
196 if (objBytes == null || other.objBytes == null)
197 return objBytes == other.objBytes;
198
199 // quick, easy test
200 if (objBytes.length != other.objBytes.length)
201 return false;
202
203 //!! There is talk about adding an array comparision method
204 //!! at 1.2 -- if so, this should be rewritten. -arnold
205 for (int i = 0; i < objBytes.length; ++i) {
206 if (objBytes[i] != other.objBytes[i])
207 return false;
208 }
209 return true;
210 } else {
211 return false;
212 }
213 }
214
215 /**
216 * This class is used to marshal objects for
217 * <code>MarshalledObject</code>. It places the location annotations
218 * to one side so that two <code>MarshalledObject</code>s can be
219 * compared for equality if they differ only in location
220 * annotations. Objects written using this stream should be read back
221 * from a <code>MarshalledObjectInputStream</code>.
222 *
223 * @see java.rmi.MarshalledObject
224 * @see MarshalledObjectInputStream
225 */
226 private static class MarshalledObjectOutputStream
227 extends MarshalOutputStream
228 {
229 /** The stream on which location objects are written. */
230 private ObjectOutputStream locOut;
231
232 /** <code>true</code> if non-<code>null</code> annotations are
233 * written.
234 */
235 private boolean hadAnnotations;
236
237 /**
238 * Creates a new <code>MarshalledObjectOutputStream</code> whose
239 * non-location bytes will be written to <code>objOut</code> and whose
240 * location annotations (if any) will be written to
241 * <code>locOut</code>.
242 */
243 MarshalledObjectOutputStream(OutputStream objOut, OutputStream locOut)
244 throws IOException
245 {
246 super(objOut);
247 this.useProtocolVersion(ObjectStreamConstants.PROTOCOL_VERSION_2);
248 this.locOut = new ObjectOutputStream(locOut);
249 hadAnnotations = false;
250 }
251
252 /**
253 * Returns <code>true</code> if any non-<code>null</code> location
254 * annotations have been written to this stream.
255 */
256 boolean hadAnnotations() {
257 return hadAnnotations;
258 }
259
260 /**
261 * Overrides MarshalOutputStream.writeLocation implementation to write
262 * annotations to the location stream.
263 */
264 protected void writeLocation(String loc) throws IOException {
265 hadAnnotations |= (loc != null);
266 locOut.writeObject(loc);
267 }
268
269
270 public void flush() throws IOException {
271 super.flush();
272 locOut.flush();
273 }
274 }
275
276 /**
277 * The counterpart to <code>MarshalledObjectOutputStream</code>.
278 *
279 * @see MarshalledObjectOutputStream
280 */
281 private static class MarshalledObjectInputStream
282 extends MarshalInputStream
283 {
284 /**
285 * The stream from which annotations will be read. If this is
286 * <code>null</code>, then all annotations were <code>null</code>.
287 */
288 private ObjectInputStream locIn;
289
290 /**
291 * Creates a new <code>MarshalledObjectInputStream</code> that
292 * reads its objects from <code>objIn</code> and annotations
293 * from <code>locIn</code>. If <code>locIn</code> is
294 * <code>null</code>, then all annotations will be
295 * <code>null</code>.
296 */
297 MarshalledObjectInputStream(InputStream objIn, InputStream locIn)
298 throws IOException
299 {
300 super(objIn);
301 this.locIn = (locIn == null ? null : new ObjectInputStream(locIn));
302 }
303
304 /**
305 * Overrides MarshalInputStream.readLocation to return locations from
306 * the stream we were given, or <code>null</code> if we were given a
307 * <code>null</code> location stream.
308 */
309 protected Object readLocation()
310 throws IOException, ClassNotFoundException
311 {
312 return (locIn == null ? null : locIn.readObject());
313 }
314 }
315
316}