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         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 }