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      */
ActivatableRef()60     public ActivatableRef()
61     {}
62 
63     /**
64      * Create a ActivatableRef with the specified id
65      */
ActivatableRef(ActivationID id, RemoteRef ref)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      */
getStub(ActivationDesc desc, ActivationID id)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      */
invoke(Remote obj, java.lang.reflect.Method method, Object[] params, long opnum)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      */
getRef()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      */
activate(boolean force)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      */
newCall(RemoteObject obj, Operation[] ops, int opnum, long hash)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      */
invoke(RemoteCall call)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      */
done(RemoteCall call)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      */
getRefClass(ObjectOutput out)340     public String getRefClass(ObjectOutput out)
341     {
342         return "ActivatableRef";
343     }
344 
345     /**
346      * Write out external representation for remote ref.
347      */
writeExternal(ObjectOutput out)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      */
readExternal(ObjectInput in)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.isEmpty()) 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      */
remoteToString()392     public String remoteToString() {
393         return Util.getUnqualifiedName(getClass()) +
394                 " [remoteRef: " + ref + "]";
395     }
396 
397     /**
398      * default implementation of hashCode for remote objects
399      */
remoteHashCode()400     public int remoteHashCode() {
401         return id.hashCode();
402     }
403 
404     /** default implementation of equals for remote objects
405      */
remoteEquals(RemoteRef ref)406     public boolean remoteEquals(RemoteRef ref) {
407         if (ref instanceof ActivatableRef)
408             return id.equals(((ActivatableRef)ref).id);
409         return false;
410     }
411 }
412