1 /*
   2  * Copyright (c) 1997, 2003, 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 public class ActivatableRef implements RemoteRef {
  45 
  46     private static final long serialVersionUID = 7579060052569229166L;
  47 
  48     protected ActivationID id;
  49     protected RemoteRef ref;
  50     transient boolean force = false;
  51 
  52     private static final int MAX_RETRIES = 3;
  53     private static final String versionComplaint =
  54         "activation requires 1.2 stubs";
  55 
  56     /**
  57      * Create a new (empty) ActivatableRef
  58      */
  59     public ActivatableRef()
  60     {}
  61 
  62     /**
  63      * Create a ActivatableRef with the specified id
  64      */
  65     public ActivatableRef(ActivationID id, RemoteRef ref)
  66     {
  67         this.id = id;
  68         this.ref = ref;
  69     }
  70 
  71     /**
  72      * Returns the stub for the remote object whose class is
  73      * specified in the activation descriptor. The ActivatableRef
  74      * in the resulting stub has its activation id set to the
  75      * activation id supplied as the second argument.
  76      */
  77     public static Remote getStub(ActivationDesc desc, ActivationID id)
  78         throws StubNotFoundException
  79     {
  80         String className = desc.getClassName();
  81 
  82         try {
  83             Class<?> cl =
  84                 RMIClassLoader.loadClass(desc.getLocation(), className);
  85             RemoteRef clientRef = new ActivatableRef(id, null);
  86             return Util.createProxy(cl, clientRef, false);
  87 
  88         } catch (IllegalArgumentException e) {
  89             throw new StubNotFoundException(
  90                 "class implements an illegal remote interface", e);
  91 
  92         } catch (ClassNotFoundException e) {
  93             throw new StubNotFoundException("unable to load class: " +
  94                                             className, e);
  95         } catch (MalformedURLException e) {
  96             throw new StubNotFoundException("malformed URL", e);
  97         }
  98     }
  99 
 100     /**
 101      * Invoke method on remote object. This method delegates remote
 102      * method invocation to the underlying ref type.  If the
 103      * underlying reference is not known (is null), then the object
 104      * must be activated first.  If an attempt at method invocation
 105      * fails, the object should force reactivation.  Method invocation
 106      * must preserve "at most once" call semantics.  In RMI, "at most
 107      * once" applies to parameter deserialization at the remote site
 108      * and the remote object's method execution.  "At most once" does
 109      * not apply to parameter serialization at the client so the
 110      * parameters of a call don't need to be buffered in anticipation
 111      * of call retry. Thus, a method call is only be retried if the
 112      * initial method invocation does not execute at all at the server
 113      * (including parameter deserialization).
 114      */
 115     public Object invoke(Remote obj,
 116                          java.lang.reflect.Method method,
 117                          Object[] params,
 118                          long opnum)
 119         throws Exception
 120     {
 121 
 122         boolean force = false;
 123         RemoteRef localRef;
 124         Exception exception = null;
 125 
 126         /*
 127          * Attempt object activation if active ref is unknown.
 128          * Throws a RemoteException if object can't be activated.
 129          */
 130         synchronized (this) {
 131             if (ref == null) {
 132                 localRef = activate(force);
 133                 force = true;
 134             } else {
 135                 localRef = ref;
 136             }
 137         }
 138 
 139         for (int retries = MAX_RETRIES; retries > 0; retries--) {
 140 
 141             try {
 142                 return localRef.invoke(obj, method, params, opnum);
 143             } catch (NoSuchObjectException e) {
 144                 /*
 145                  * Object is not active in VM; retry call
 146                  */
 147                 exception = e;
 148             } catch (ConnectException e) {
 149                 /*
 150                  * Failure during connection setup; retry call
 151                  */
 152                 exception = e;
 153             } catch (UnknownHostException e) {
 154                 /*
 155                  * Failure during connection setup; retry call.
 156                  */
 157                 exception = e;
 158             } catch (ConnectIOException e) {
 159                 /*
 160                  * Failure setting up multiplexed connection or reusing
 161                  * 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 }