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