blob: 94af9b01bc86b17525ee0bf436894b777b2e527f [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-2003 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.rmi.server;
27
28import java.io.IOException;
29import java.io.ObjectInput;
30import java.io.ObjectOutput;
31import java.lang.reflect.Proxy;
32import java.net.MalformedURLException;
33import java.net.URL;
34import java.rmi.*;
35import java.rmi.activation.*;
36import java.rmi.server.Operation;
37import java.rmi.server.RMIClassLoader;
38import java.rmi.server.RemoteCall;
39import java.rmi.server.RemoteObject;
40import java.rmi.server.RemoteObjectInvocationHandler;
41import java.rmi.server.RemoteRef;
42import java.rmi.server.RemoteStub;
43
44public 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}