1 /*
   2  * Copyright (c) 1997, 2005, Oracle and/or its affiliates. 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.rmi;
  27 
  28 import java.io.ByteArrayInputStream;
  29 import java.io.ByteArrayOutputStream;
  30 import java.io.IOException;
  31 import java.io.InputStream;
  32 import java.io.ObjectInputStream;
  33 import java.io.ObjectOutputStream;
  34 import java.io.ObjectStreamConstants;
  35 import java.io.OutputStream;
  36 import java.io.Serializable;
  37 import sun.rmi.server.MarshalInputStream;
  38 import 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  */
  71 public 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         @SuppressWarnings("unchecked")
 160         T obj = (T) in.readObject();
 161         in.close();
 162         return obj;
 163     }
 164 
 165     /**
 166      * Return a hash code for this <code>MarshalledObject</code>.
 167      *
 168      * @return a hash code
 169      */
 170     public int hashCode() {
 171         return hash;
 172     }
 173 
 174     /**
 175      * Compares this <code>MarshalledObject</code> to another object.
 176      * Returns true if and only if the argument refers to a
 177      * <code>MarshalledObject</code> that contains exactly the same
 178      * serialized representation of an object as this one does. The
 179      * comparison ignores any class codebase annotation, meaning that
 180      * two objects are equivalent if they have the same serialized
 181      * representation <i>except</i> for the codebase of each class
 182      * in the serialized representation.
 183      *
 184      * @param obj the object to compare with this <code>MarshalledObject</code>
 185      * @return <code>true</code> if the argument contains an equaivalent
 186      * serialized object; <code>false</code> otherwise
 187      * @since 1.2
 188      */
 189     public boolean equals(Object obj) {
 190         if (obj == this)
 191             return true;
 192 
 193         if (obj != null && obj instanceof MarshalledObject) {
 194             MarshalledObject<?> other = (MarshalledObject<?>) obj;
 195 
 196             // if either is a ref to null, both must be
 197             if (objBytes == null || other.objBytes == null)
 198                 return objBytes == other.objBytes;
 199 
 200             // quick, easy test
 201             if (objBytes.length != other.objBytes.length)
 202                 return false;
 203 
 204             //!! There is talk about adding an array comparision method
 205             //!! at 1.2 -- if so, this should be rewritten.  -arnold
 206             for (int i = 0; i < objBytes.length; ++i) {
 207                 if (objBytes[i] != other.objBytes[i])
 208                     return false;
 209             }
 210             return true;
 211         } else {
 212             return false;
 213         }
 214     }
 215 
 216     /**
 217      * This class is used to marshal objects for
 218      * <code>MarshalledObject</code>.  It places the location annotations
 219      * to one side so that two <code>MarshalledObject</code>s can be
 220      * compared for equality if they differ only in location
 221      * annotations.  Objects written using this stream should be read back
 222      * from a <code>MarshalledObjectInputStream</code>.
 223      *
 224      * @see java.rmi.MarshalledObject
 225      * @see MarshalledObjectInputStream
 226      */
 227     private static class MarshalledObjectOutputStream
 228         extends MarshalOutputStream
 229     {
 230         /** The stream on which location objects are written. */
 231         private ObjectOutputStream locOut;
 232 
 233         /** <code>true</code> if non-<code>null</code> annotations are
 234          *  written.
 235          */
 236         private boolean hadAnnotations;
 237 
 238         /**
 239          * Creates a new <code>MarshalledObjectOutputStream</code> whose
 240          * non-location bytes will be written to <code>objOut</code> and whose
 241          * location annotations (if any) will be written to
 242          * <code>locOut</code>.
 243          */
 244         MarshalledObjectOutputStream(OutputStream objOut, OutputStream locOut)
 245             throws IOException
 246         {
 247             super(objOut);
 248             this.useProtocolVersion(ObjectStreamConstants.PROTOCOL_VERSION_2);
 249             this.locOut = new ObjectOutputStream(locOut);
 250             hadAnnotations = false;
 251         }
 252 
 253         /**
 254          * Returns <code>true</code> if any non-<code>null</code> location
 255          * annotations have been written to this stream.
 256          */
 257         boolean hadAnnotations() {
 258             return hadAnnotations;
 259         }
 260 
 261         /**
 262          * Overrides MarshalOutputStream.writeLocation implementation to write
 263          * annotations to the location stream.
 264          */
 265         protected void writeLocation(String loc) throws IOException {
 266             hadAnnotations |= (loc != null);
 267             locOut.writeObject(loc);
 268         }
 269 
 270 
 271         public void flush() throws IOException {
 272             super.flush();
 273             locOut.flush();
 274         }
 275     }
 276 
 277     /**
 278      * The counterpart to <code>MarshalledObjectOutputStream</code>.
 279      *
 280      * @see MarshalledObjectOutputStream
 281      */
 282     private static class MarshalledObjectInputStream
 283         extends MarshalInputStream
 284     {
 285         /**
 286          * The stream from which annotations will be read.  If this is
 287          * <code>null</code>, then all annotations were <code>null</code>.
 288          */
 289         private ObjectInputStream locIn;
 290 
 291         /**
 292          * Creates a new <code>MarshalledObjectInputStream</code> that
 293          * reads its objects from <code>objIn</code> and annotations
 294          * from <code>locIn</code>.  If <code>locIn</code> is
 295          * <code>null</code>, then all annotations will be
 296          * <code>null</code>.
 297          */
 298         MarshalledObjectInputStream(InputStream objIn, InputStream locIn)
 299             throws IOException
 300         {
 301             super(objIn);
 302             this.locIn = (locIn == null ? null : new ObjectInputStream(locIn));
 303         }
 304 
 305         /**
 306          * Overrides MarshalInputStream.readLocation to return locations from
 307          * the stream we were given, or <code>null</code> if we were given a
 308          * <code>null</code> location stream.
 309          */
 310         protected Object readLocation()
 311             throws IOException, ClassNotFoundException
 312         {
 313             return (locIn == null ? null : locIn.readObject());
 314         }
 315     }
 316 
 317 }