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 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 setting up multiplexed connection or reusing
162                  * cached connection; retry call
163                  */
164                 exception = e;
165             } catch (MarshalException e) {
166                 /*
167                  * Failure during parameter serialization; call may
168                  * have reached server, so call retry not possible.
169                  */
170                 throw e;
171             } catch (ServerError e) {
172                 /*
173                  * Call reached server; propagate remote exception.
174                  */
175                 throw e;
176             } catch (ServerException e) {
177                 /*
178                  * Call reached server; propagate remote exception
179                  */
180                 throw e;
181             } catch (RemoteException e) {
182                 /*
183                  * This is a catch-all for other RemoteExceptions.
184                  * UnmarshalException being the only one relevant.
185                  *
186                  * StubNotFoundException should never show up because
187                  * it is generally thrown when attempting to locate
188                  * a stub.
189                  *
190                  * UnexpectedException should never show up because
191                  * it is only thrown by a stub and would be wrapped
192                  * in a ServerException if it was propagated by a
193                  * remote call.
194                  */
195                 synchronized (this) {
196                     if (localRef == ref) {
197                         ref = null;     // this may be overly conservative
198                     }
199                 }
200 
201                 throw e;
202             }
203 
204             if (retries > 1) {
205                 /*
206                  * Activate object, since object could not be reached.
207                  */
208                 synchronized (this) {
209                     if (localRef.remoteEquals(ref) || ref == null) {
210                         RemoteRef newRef = activate(force);
211 
212                         if (newRef.remoteEquals(localRef) &&
213                             exception instanceof NoSuchObjectException &&
214                             force == false) {
215                             /*
216                              * If last exception was NoSuchObjectException,
217                              * then old value of ref is definitely wrong,
218                              * so make sure that it is different.
219                              */
220                             newRef = activate(true);
221                         }
222 
223                         localRef = newRef;
224                         force = true;
225                     } else {
226                         localRef = ref;
227                         force = false;
228                     }
229                 }
230             }
231         }
232 
233         /*
234          * Retries unsuccessful, so throw last exception
235          */
236         throw exception;
237     }
238 
239     /**
240      * private method to obtain the ref for a call.
241      */
getRef()242     private synchronized RemoteRef getRef()
243         throws RemoteException
244     {
245         if (ref == null) {
246             ref = activate(false);
247         }
248 
249         return ref;
250     }
251 
252     /**
253      * private method to activate the remote object.
254      *
255      * NOTE: the caller must be synchronized on "this" before
256      * calling this method.
257      */
activate(boolean force)258     private RemoteRef activate(boolean force)
259         throws RemoteException
260     {
261         assert Thread.holdsLock(this);
262 
263         ref = null;
264         try {
265             /*
266              * Activate the object and retrieve the remote reference
267              * from inside the stub returned as the result. Then
268              * set this activatable ref's internal ref to be the
269              * ref inside the ref of the stub. In more clear terms,
270              * the stub returned from the activate call contains an
271              * ActivatableRef. We need to set the ref in *this*
272              * ActivatableRef to the ref inside the ActivatableRef
273              * retrieved from the stub. The ref type embedded in the
274              * ActivatableRef is typically a UnicastRef.
275              */
276 
277             Remote proxy = id.activate(force);
278             ActivatableRef newRef = null;
279 
280             if (proxy instanceof RemoteStub) {
281                 newRef = (ActivatableRef) ((RemoteStub) proxy).getRef();
282             } else {
283                 /*
284                  * Assume that proxy is an instance of a dynamic proxy
285                  * class.  If that assumption is not correct, or either of
286                  * the casts below fails, the resulting exception will be
287                  * wrapped in an ActivateFailedException below.
288                  */
289                 RemoteObjectInvocationHandler handler =
290                     (RemoteObjectInvocationHandler)
291                     Proxy.getInvocationHandler(proxy);
292                 newRef = (ActivatableRef) handler.getRef();
293             }
294             ref = newRef.ref;
295             return ref;
296 
297         } catch (ConnectException e) {
298             throw new ConnectException("activation failed", e);
299         } catch (RemoteException e) {
300             throw new ConnectIOException("activation failed", e);
301         } catch (UnknownObjectException e) {
302             throw new NoSuchObjectException("object not registered");
303         } catch (ActivationException e) {
304             throw new ActivateFailedException("activation failed", e);
305         }
306     }
307 
308     /**
309      * This call is used by the old 1.1 stub protocol and is
310      * unsupported since activation requires 1.2 stubs.
311      */
newCall(RemoteObject obj, Operation[] ops, int opnum, long hash)312     public synchronized RemoteCall newCall(RemoteObject obj,
313                                            Operation[] ops,
314                                            int opnum,
315                                            long hash)
316         throws RemoteException
317     {
318         throw new UnsupportedOperationException(versionComplaint);
319     }
320 
321     /**
322      * This call is used by the old 1.1 stub protocol and is
323      * unsupported since activation requires 1.2 stubs.
324      */
invoke(RemoteCall call)325     public void invoke(RemoteCall call) throws Exception
326     {
327         throw new UnsupportedOperationException(versionComplaint);
328     }
329 
330     /**
331      * This call is used by the old 1.1 stub protocol and is
332      * unsupported since activation requires 1.2 stubs.
333      */
done(RemoteCall call)334     public void done(RemoteCall call) throws RemoteException {
335         throw new UnsupportedOperationException(versionComplaint);
336     }
337 
338     /**
339      * Returns the class of the ref type to be serialized
340      */
getRefClass(ObjectOutput out)341     public String getRefClass(ObjectOutput out)
342     {
343         return "ActivatableRef";
344     }
345 
346     /**
347      * Write out external representation for remote ref.
348      */
writeExternal(ObjectOutput out)349     public void writeExternal(ObjectOutput out) throws IOException
350     {
351         RemoteRef localRef = ref;
352 
353         out.writeObject(id);
354         if (localRef == null) {
355             out.writeUTF("");
356         } else {
357             out.writeUTF(localRef.getRefClass(out));
358             localRef.writeExternal(out);
359         }
360     }
361 
362     /**
363      * Read in external representation for remote ref.
364      * @exception ClassNotFoundException If the class for an object
365      * being restored cannot be found.
366      */
readExternal(ObjectInput in)367     public void readExternal(ObjectInput in)
368         throws IOException, ClassNotFoundException
369     {
370         id = (ActivationID)in.readObject();
371         ref = null;
372         String className = in.readUTF();
373 
374         if (className.equals("")) return;
375 
376         try {
377             Class<?> refClass = Class.forName(RemoteRef.packagePrefix + "." +
378                                               className);
379             ref = (RemoteRef)refClass.newInstance();
380             ref.readExternal(in);
381         } catch (InstantiationException e) {
382             throw new UnmarshalException("Unable to create remote reference",
383                                          e);
384         } catch (IllegalAccessException e) {
385             throw new UnmarshalException("Illegal access creating remote reference");
386         }
387     }
388 
389     //----------------------------------------------------------------------;
390     /**
391      * Method from object, forward from RemoteObject
392      */
remoteToString()393     public String remoteToString() {
394         return Util.getUnqualifiedName(getClass()) +
395                 " [remoteRef: " + ref + "]";
396     }
397 
398     /**
399      * default implementation of hashCode for remote objects
400      */
remoteHashCode()401     public int remoteHashCode() {
402         return id.hashCode();
403     }
404 
405     /** default implementation of equals for remote objects
406      */
remoteEquals(RemoteRef ref)407     public boolean remoteEquals(RemoteRef ref) {
408         if (ref instanceof ActivatableRef)
409             return id.equals(((ActivatableRef)ref).id);
410         return false;
411     }
412 }
413