1 /*
   2  * Copyright (c) 1997, 2013, 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.activation;
  27 
  28 import java.io.IOException;
  29 import java.io.InvalidObjectException;
  30 import java.io.ObjectInputStream;
  31 import java.io.ObjectOutputStream;
  32 import java.io.Serializable;
  33 import java.lang.reflect.InvocationHandler;
  34 import java.lang.reflect.Proxy;
  35 import java.rmi.MarshalledObject;
  36 import java.rmi.Remote;
  37 import java.rmi.RemoteException;
  38 import java.rmi.UnmarshalException;
  39 import java.rmi.server.RemoteObject;
  40 import java.rmi.server.RemoteObjectInvocationHandler;
  41 import java.rmi.server.RemoteRef;
  42 import java.rmi.server.UID;
  43 
  44 /**
  45  * Activation makes use of special identifiers to denote remote
  46  * objects that can be activated over time. An activation identifier
  47  * (an instance of the class <code>ActivationID</code>) contains several
  48  * pieces of information needed for activating an object:
  49  * <ul>
  50  * <li> a remote reference to the object's activator (a {@link
  51  * java.rmi.server.RemoteRef RemoteRef}
  52  * instance), and
  53  * <li> a unique identifier (a {@link java.rmi.server.UID UID}
  54  * instance) for the object. </ul> <p>
  55  *
  56  * An activation identifier for an object can be obtained by registering
  57  * an object with the activation system. Registration is accomplished
  58  * in a few ways: <ul>
  59  * <li>via the <code>Activatable.register</code> method
  60  * <li>via the first <code>Activatable</code> constructor (that takes
  61  * three arguments and both registers and exports the object, and
  62  * <li>via the first <code>Activatable.exportObject</code> method
  63  * that takes the activation descriptor, object and port as arguments;
  64  * this method both registers and exports the object. </ul>
  65  *
  66  * @author      Ann Wollrath
  67  * @see         Activatable
  68  * @since       1.2
  69  */
  70 public class ActivationID implements Serializable {
  71     /**
  72      * the object's activator
  73      */
  74     private transient Activator activator;
  75 
  76     /**
  77      * the object's unique id
  78      */
  79     private transient UID uid = new UID();
  80 
  81     /** indicate compatibility with the Java 2 SDK v1.2 version of class */
  82     private static final long serialVersionUID = -4608673054848209235L;
  83 
  84     /**
  85      * The constructor for <code>ActivationID</code> takes a single
  86      * argument, activator, that specifies a remote reference to the
  87      * activator responsible for activating the object associated with
  88      * this identifier. An instance of <code>ActivationID</code> is globally
  89      * unique.
  90      *
  91      * @param activator reference to the activator responsible for
  92      * activating the object
  93      * @throws UnsupportedOperationException if and only if activation is
  94      *         not supported by this implementation
  95      * @since 1.2
  96      */
  97     public ActivationID(Activator activator) {
  98         this.activator = activator;
  99     }
 100 
 101     /**
 102      * Activate the object for this id.
 103      *
 104      * @param force if true, forces the activator to contact the group
 105      * when activating the object (instead of returning a cached reference);
 106      * if false, returning a cached value is acceptable.
 107      * @return the reference to the active remote object
 108      * @exception ActivationException if activation fails
 109      * @exception UnknownObjectException if the object is unknown
 110      * @exception RemoteException if remote call fails
 111      * @since 1.2
 112      */
 113     public Remote activate(boolean force)
 114         throws ActivationException, UnknownObjectException, RemoteException
 115     {
 116         try {
 117             MarshalledObject<? extends Remote> mobj =
 118                 activator.activate(this, force);
 119             return mobj.get();
 120         } catch (RemoteException e) {
 121             throw e;
 122         } catch (IOException e) {
 123             throw new UnmarshalException("activation failed", e);
 124         } catch (ClassNotFoundException e) {
 125             throw new UnmarshalException("activation failed", e);
 126         }
 127 
 128     }
 129 
 130     /**
 131      * Returns a hashcode for the activation id.  Two identifiers that
 132      * refer to the same remote object will have the same hash code.
 133      *
 134      * @see java.util.Hashtable
 135      * @since 1.2
 136      */
 137     public int hashCode() {
 138         return uid.hashCode();
 139     }
 140 
 141     /**
 142      * Compares two activation ids for content equality.
 143      * Returns true if both of the following conditions are true:
 144      * 1) the unique identifiers equivalent (by content), and
 145      * 2) the activator specified in each identifier
 146      *    refers to the same remote object.
 147      *
 148      * @param   obj     the Object to compare with
 149      * @return  true if these Objects are equal; false otherwise.
 150      * @see             java.util.Hashtable
 151      * @since 1.2
 152      */
 153     public boolean equals(Object obj) {
 154         if (obj instanceof ActivationID) {
 155             ActivationID id = (ActivationID) obj;
 156             return (uid.equals(id.uid) && activator.equals(id.activator));
 157         } else {
 158             return false;
 159         }
 160     }
 161 
 162     /**
 163      * <code>writeObject</code> for custom serialization.
 164      *
 165      * <p>This method writes this object's serialized form for
 166      * this class as follows:
 167      *
 168      * <p>The <code>writeObject</code> method is invoked on
 169      * <code>out</code> passing this object's unique identifier
 170      * (a {@link java.rmi.server.UID UID} instance) as the argument.
 171      *
 172      * <p>Next, the {@link
 173      * java.rmi.server.RemoteRef#getRefClass(java.io.ObjectOutput)
 174      * getRefClass} method is invoked on the activator's
 175      * <code>RemoteRef</code> instance to obtain its external ref
 176      * type name.  Next, the <code>writeUTF</code> method is
 177      * invoked on <code>out</code> with the value returned by
 178      * <code>getRefClass</code>, and then the
 179      * <code>writeExternal</code> method is invoked on the
 180      * <code>RemoteRef</code> instance passing <code>out</code>
 181      * as the argument.
 182      *
 183      * @serialData The serialized data for this class comprises a
 184      * <code>java.rmi.server.UID</code> (written with
 185      * <code>ObjectOutput.writeObject</code>) followed by the
 186      * external ref type name of the activator's
 187      * <code>RemoteRef</code> instance (a string written with
 188      * <code>ObjectOutput.writeUTF</code>), followed by the
 189      * external form of the <code>RemoteRef</code> instance as
 190      * written by its <code>writeExternal</code> method.
 191      *
 192      * <p>The external ref type name of the
 193      * <code>RemoteRef</Code> instance is
 194      * determined using the definitions of external ref type
 195      * names specified in the {@link java.rmi.server.RemoteObject
 196      * RemoteObject} <code>writeObject</code> method
 197      * <b>serialData</b> specification.  Similarly, the data
 198      * written by the <code>writeExternal</code> method and read
 199      * by the <code>readExternal</code> method of
 200      * <code>RemoteRef</code> implementation classes
 201      * corresponding to each of the defined external ref type
 202      * names is specified in the {@link
 203      * java.rmi.server.RemoteObject RemoteObject}
 204      * <code>writeObject</code> method <b>serialData</b>
 205      * specification.
 206      **/
 207     private void writeObject(ObjectOutputStream out)
 208         throws IOException, ClassNotFoundException
 209     {
 210         out.writeObject(uid);
 211 
 212         RemoteRef ref;
 213         if (activator instanceof RemoteObject) {
 214             ref = ((RemoteObject) activator).getRef();
 215         } else if (Proxy.isProxyClass(activator.getClass())) {
 216             InvocationHandler handler = Proxy.getInvocationHandler(activator);
 217             if (!(handler instanceof RemoteObjectInvocationHandler)) {
 218                 throw new InvalidObjectException(
 219                     "unexpected invocation handler");
 220             }
 221             ref = ((RemoteObjectInvocationHandler) handler).getRef();
 222 
 223         } else {
 224             throw new InvalidObjectException("unexpected activator type");
 225         }
 226         out.writeUTF(ref.getRefClass(out));
 227         ref.writeExternal(out);
 228     }
 229 
 230     /**
 231      * <code>readObject</code> for custom serialization.
 232      *
 233      * <p>This method reads this object's serialized form for this
 234      * class as follows:
 235      *
 236      * <p>The <code>readObject</code> method is invoked on
 237      * <code>in</code> to read this object's unique identifier
 238      * (a {@link java.rmi.server.UID UID} instance).
 239      *
 240      * <p>Next, the <code>readUTF</code> method is invoked on
 241      * <code>in</code> to read the external ref type name of the
 242      * <code>RemoteRef</code> instance for this object's
 243      * activator.  Next, the <code>RemoteRef</code>
 244      * instance is created of an implementation-specific class
 245      * corresponding to the external ref type name (returned by
 246      * <code>readUTF</code>), and the <code>readExternal</code>
 247      * method is invoked on that <code>RemoteRef</code> instance
 248      * to read the external form corresponding to the external
 249      * ref type name.
 250      *
 251      * <p>Note: If the external ref type name is
 252      * <code>"UnicastRef"</code>, <code>"UnicastServerRef"</code>,
 253      * <code>"UnicastRef2"</code>, <code>"UnicastServerRef2"</code>,
 254      * or <code>"ActivatableRef"</code>, a corresponding
 255      * implementation-specific class must be found, and its
 256      * <code>readExternal</code> method must read the serial data
 257      * for that external ref type name as specified to be written
 258      * in the <b>serialData</b> documentation for this class.
 259      * If the external ref type name is any other string (of non-zero
 260      * length), a <code>ClassNotFoundException</code> will be thrown,
 261      * unless the implementation provides an implementation-specific
 262      * class corresponding to that external ref type name, in which
 263      * case the <code>RemoteRef</code> will be an instance of
 264      * that implementation-specific class.
 265      */
 266     private void readObject(ObjectInputStream in)
 267         throws IOException, ClassNotFoundException
 268     {
 269         uid = (UID)in.readObject();
 270 
 271         try {
 272             Class<? extends RemoteRef> refClass =
 273                 Class.forName(RemoteRef.packagePrefix + "." + in.readUTF())
 274                 .asSubclass(RemoteRef.class);
 275             @SuppressWarnings("deprecation")
 276             RemoteRef ref = refClass.newInstance();
 277             ref.readExternal(in);
 278             activator = (Activator)
 279                 Proxy.newProxyInstance(Activator.class.getClassLoader(),
 280                                        new Class<?>[] { Activator.class },
 281                                        new RemoteObjectInvocationHandler(ref));
 282         } catch (InstantiationException e) {
 283             throw (IOException)
 284                 new InvalidObjectException(
 285                     "Unable to create remote reference").initCause(e);
 286         } catch (IllegalAccessException e) {
 287             throw (IOException)
 288                 new InvalidObjectException(
 289                     "Unable to create remote reference").initCause(e);
 290         }
 291     }
 292 }