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 sun.rmi.server;
  27 
  28 import java.io.IOException;
  29 import java.io.ObjectInput;
  30 import java.io.ObjectOutput;
  31 import java.lang.reflect.Proxy;
  32 import java.net.MalformedURLException;
  33 import java.net.URL;
  34 import java.rmi.*;
  35 import java.rmi.activation.*;
  36 import java.rmi.server.Operation;
  37 import java.rmi.server.RMIClassLoader;
  38 import java.rmi.server.RemoteCall;
  39 import java.rmi.server.RemoteObject;
  40 import java.rmi.server.RemoteObjectInvocationHandler;
  41 import java.rmi.server.RemoteRef;
  42 import java.rmi.server.RemoteStub;
  43 
  44 @SuppressWarnings("deprecation")
  45 public class ActivatableRef implements RemoteRef {
  46 
  47     private static final long serialVersionUID = 7579060052569229166L;
  48 
  49     protected ActivationID id;
  50     protected RemoteRef ref;
  51     transient boolean force = false;
  52 
  53     private static final int MAX_RETRIES = 3;
  54     private static final String versionComplaint =
  55         "activation requires 1.2 stubs";
  56 
  57     /**
  58      * Create a new (empty) ActivatableRef
  59      */
  60     public ActivatableRef()
  61     {}
  62 
  63     /**
  64      * Create a ActivatableRef with the specified id
  65      */
  66     public ActivatableRef(ActivationID id, RemoteRef ref)
  67     {
  68         this.id = id;
  69         this.ref = ref;
  70     }
  71 
  72     /**
  73      * Returns the stub for the remote object whose class is
  74      * specified in the activation descriptor. The ActivatableRef
  75      * in the resulting stub has its activation id set to the
  76      * activation id supplied as the second argument.
  77      */
  78     public static Remote getStub(ActivationDesc desc, ActivationID id)
  79         throws StubNotFoundException
  80     {
  81         String className = desc.getClassName();
  82 
  83         try {
  84             Class<?> cl =
  85                 RMIClassLoader.loadClass(desc.getLocation(), className);
  86             RemoteRef clientRef = new ActivatableRef(id, null);
  87             return Util.createProxy(cl, clientRef, false);
  88 
  89         } catch (IllegalArgumentException e) {
  90             throw new StubNotFoundException(
  91                 "class implements an illegal remote interface", e);
  92 
  93         } catch (ClassNotFoundException e) {
  94             throw new StubNotFoundException("unable to load class: " +
  95                                             className, e);
  96         } catch (MalformedURLException e) {
  97             throw new StubNotFoundException("malformed URL", e);
  98         }
  99     }
 100 
 101     /**
 102      * Invoke method on remote object. This method delegates remote
 103      * method invocation to the underlying ref type.  If the
 104      * underlying reference is not known (is null), then the object
 105      * must be activated first.  If an attempt at method invocation
 106      * fails, the object should force reactivation.  Method invocation
 107      * must preserve "at most once" call semantics.  In RMI, "at most
 108      * once" applies to parameter deserialization at the remote site
 109      * and the remote object's method execution.  "At most once" does
 110      * not apply to parameter serialization at the client so the
 111      * parameters of a call don't need to be buffered in anticipation
 112      * of call retry. Thus, a method call is only be retried if the
 113      * initial method invocation does not execute at all at the server
 114      * (including parameter deserialization).
 115      */
 116     public Object invoke(Remote obj,
 117                          java.lang.reflect.Method method,
 118                          Object[] params,
 119                          long opnum)
 120         throws Exception
 121     {
 122 
 123         boolean force = false;
 124         RemoteRef localRef;
 125         Exception exception = null;
 126 
 127         /*
 128          * Attempt object activation if active ref is unknown.
 129          * Throws a RemoteException if object can't be activated.
 130          */
 131         synchronized (this) {
 132             if (ref == null) {
 133                 localRef = activate(force);
 134                 force = true;
 135             } else {
 136                 localRef = ref;
 137             }
 138         }
 139 
 140         for (int retries = MAX_RETRIES; retries > 0; retries--) {
 141 
 142             try {
 143                 return localRef.invoke(obj, method, params, opnum);
 144             } catch (NoSuchObjectException e) {
 145                 /*
 146                  * Object is not active in VM; retry call
 147                  */
 148                 exception = e;
 149             } catch (ConnectException e) {
 150                 /*
 151                  * Failure during connection setup; retry call
 152                  */
 153                 exception = e;
 154             } catch (UnknownHostException e) {
 155                 /*
 156                  * Failure during connection setup; retry call.
 157                  */
 158                 exception = e;
 159             } catch (ConnectIOException e) {
 160                 /*
 161                  * Failure reusing cached connection; retry call
 162                  */
 163                 exception = e;
 164             } catch (MarshalException e) {
 165                 /*
 166                  * Failure during parameter serialization; call may
 167                  * have reached server, so call retry not possible.
 168                  */
 169                 throw e;
 170             } catch (ServerError e) {
 171                 /*
 172                  * Call reached server; propagate remote exception.
 173                  */
 174                 throw e;
 175             } catch (ServerException e) {
 176                 /*
 177                  * Call reached server; propagate remote exception
 178                  */
 179                 throw e;
 180             } catch (RemoteException e) {
 181                 /*
 182                  * This is a catch-all for other RemoteExceptions.
 183                  * UnmarshalException being the only one relevant.
 184                  *
 185                  * StubNotFoundException should never show up because
 186                  * it is generally thrown when attempting to locate
 187                  * a stub.
 188                  *
 189                  * UnexpectedException should never show up because
 190                  * it is only thrown by a stub and would be wrapped
 191                  * in a ServerException if it was propagated by a
 192                  * remote call.
 193                  */
 194                 synchronized (this) {
 195                     if (localRef == ref) {
 196                         ref = null;     // this may be overly conservative
 197                     }
 198                 }
 199 
 200                 throw e;
 201             }
 202 
 203             if (retries > 1) {
 204                 /*
 205                  * Activate object, since object could not be reached.
 206                  */
 207                 synchronized (this) {
 208                     if (localRef.remoteEquals(ref) || ref == null) {
 209                         RemoteRef newRef = activate(force);
 210 
 211                         if (newRef.remoteEquals(localRef) &&
 212                             exception instanceof NoSuchObjectException &&
 213                             force == false) {
 214                             /*
 215                              * If last exception was NoSuchObjectException,
 216                              * then old value of ref is definitely wrong,
 217                              * so make sure that it is different.
 218                              */
 219                             newRef = activate(true);
 220                         }
 221 
 222                         localRef = newRef;
 223                         force = true;
 224                     } else {
 225                         localRef = ref;
 226                         force = false;
 227                     }
 228                 }
 229             }
 230         }
 231 
 232         /*
 233          * Retries unsuccessful, so throw last exception
 234          */
 235         throw exception;
 236     }
 237 
 238     /**
 239      * private method to obtain the ref for a call.
 240      */
 241     private synchronized RemoteRef getRef()
 242         throws RemoteException
 243     {
 244         if (ref == null) {
 245             ref = activate(false);
 246         }
 247 
 248         return ref;
 249     }
 250 
 251     /**
 252      * private method to activate the remote object.
 253      *
 254      * NOTE: the caller must be synchronized on "this" before
 255      * calling this method.
 256      */
 257     private RemoteRef activate(boolean force)
 258         throws RemoteException
 259     {
 260         assert Thread.holdsLock(this);
 261 
 262         ref = null;
 263         try {
 264             /*
 265              * Activate the object and retrieve the remote reference
 266              * from inside the stub returned as the result. Then
 267              * set this activatable ref's internal ref to be the
 268              * ref inside the ref of the stub. In more clear terms,
 269              * the stub returned from the activate call contains an
 270              * ActivatableRef. We need to set the ref in *this*
 271              * ActivatableRef to the ref inside the ActivatableRef
 272              * retrieved from the stub. The ref type embedded in the
 273              * ActivatableRef is typically a UnicastRef.
 274              */
 275 
 276             Remote proxy = id.activate(force);
 277             ActivatableRef newRef = null;
 278 
 279             if (proxy instanceof RemoteStub) {
 280                 newRef = (ActivatableRef) ((RemoteStub) proxy).getRef();
 281             } else {
 282                 /*
 283                  * Assume that proxy is an instance of a dynamic proxy
 284                  * class.  If that assumption is not correct, or either of
 285                  * the casts below fails, the resulting exception will be
 286                  * wrapped in an ActivateFailedException below.
 287                  */
 288                 RemoteObjectInvocationHandler handler =
 289                     (RemoteObjectInvocationHandler)
 290                     Proxy.getInvocationHandler(proxy);
 291                 newRef = (ActivatableRef) handler.getRef();
 292             }
 293             ref = newRef.ref;
 294             return ref;
 295 
 296         } catch (ConnectException e) {
 297             throw new ConnectException("activation failed", e);
 298         } catch (RemoteException e) {
 299             throw new ConnectIOException("activation failed", e);
 300         } catch (UnknownObjectException e) {
 301             throw new NoSuchObjectException("object not registered");
 302         } catch (ActivationException e) {
 303             throw new ActivateFailedException("activation failed", e);
 304         }
 305     }
 306 
 307     /**
 308      * This call is used by the old 1.1 stub protocol and is
 309      * unsupported since activation requires 1.2 stubs.
 310      */
 311     public synchronized RemoteCall newCall(RemoteObject obj,
 312                                            Operation[] ops,
 313                                            int opnum,
 314                                            long hash)
 315         throws RemoteException
 316     {
 317         throw new UnsupportedOperationException(versionComplaint);
 318     }
 319 
 320     /**
 321      * This call is used by the old 1.1 stub protocol and is
 322      * unsupported since activation requires 1.2 stubs.
 323      */
 324     public void invoke(RemoteCall call) throws Exception
 325     {
 326         throw new UnsupportedOperationException(versionComplaint);
 327     }
 328 
 329     /**
 330      * This call is used by the old 1.1 stub protocol and is
 331      * unsupported since activation requires 1.2 stubs.
 332      */
 333     public void done(RemoteCall call) throws RemoteException {
 334         throw new UnsupportedOperationException(versionComplaint);
 335     }
 336 
 337     /**
 338      * Returns the class of the ref type to be serialized
 339      */
 340     public String getRefClass(ObjectOutput out)
 341     {
 342         return "ActivatableRef";
 343     }
 344 
 345     /**
 346      * Write out external representation for remote ref.
 347      */
 348     public void writeExternal(ObjectOutput out) throws IOException
 349     {
 350         RemoteRef localRef = ref;
 351 
 352         out.writeObject(id);
 353         if (localRef == null) {
 354             out.writeUTF("");
 355         } else {
 356             out.writeUTF(localRef.getRefClass(out));
 357             localRef.writeExternal(out);
 358         }
 359     }
 360 
 361     /**
 362      * Read in external representation for remote ref.
 363      * @exception ClassNotFoundException If the class for an object
 364      * being restored cannot be found.
 365      */
 366     public void readExternal(ObjectInput in)
 367         throws IOException, ClassNotFoundException
 368     {
 369         id = (ActivationID)in.readObject();
 370         ref = null;
 371         String className = in.readUTF();
 372 
 373         if (className.equals("")) return;
 374 
 375         try {
 376             Class<?> refClass = Class.forName(RemoteRef.packagePrefix + "." +
 377                                               className);
 378             ref = (RemoteRef)refClass.newInstance();
 379             ref.readExternal(in);
 380         } catch (InstantiationException e) {
 381             throw new UnmarshalException("Unable to create remote reference",
 382                                          e);
 383         } catch (IllegalAccessException e) {
 384             throw new UnmarshalException("Illegal access creating remote reference");
 385         }
 386     }
 387 
 388     //----------------------------------------------------------------------;
 389     /**
 390      * Method from object, forward from RemoteObject
 391      */
 392     public String remoteToString() {
 393         return Util.getUnqualifiedName(getClass()) +
 394                 " [remoteRef: " + ref + "]";
 395     }
 396 
 397     /**
 398      * default implementation of hashCode for remote objects
 399      */
 400     public int remoteHashCode() {
 401         return id.hashCode();
 402     }
 403 
 404     /** default implementation of equals for remote objects
 405      */
 406     public boolean remoteEquals(RemoteRef ref) {
 407         if (ref instanceof ActivatableRef)
 408             return id.equals(((ActivatableRef)ref).id);
 409         return false;
 410     }
 411 }