| /* |
| * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package sun.rmi.server; |
| |
| import java.io.IOException; |
| import java.io.ObjectInput; |
| import java.io.ObjectOutput; |
| import java.lang.reflect.Proxy; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.rmi.*; |
| import java.rmi.activation.*; |
| import java.rmi.server.Operation; |
| import java.rmi.server.RMIClassLoader; |
| import java.rmi.server.RemoteCall; |
| import java.rmi.server.RemoteObject; |
| import java.rmi.server.RemoteObjectInvocationHandler; |
| import java.rmi.server.RemoteRef; |
| import java.rmi.server.RemoteStub; |
| |
| @SuppressWarnings("deprecation") |
| public class ActivatableRef implements RemoteRef { |
| |
| private static final long serialVersionUID = 7579060052569229166L; |
| |
| protected ActivationID id; |
| protected RemoteRef ref; |
| transient boolean force = false; |
| |
| private static final int MAX_RETRIES = 3; |
| private static final String versionComplaint = |
| "activation requires 1.2 stubs"; |
| |
| /** |
| * Create a new (empty) ActivatableRef |
| */ |
| public ActivatableRef() |
| {} |
| |
| /** |
| * Create a ActivatableRef with the specified id |
| */ |
| public ActivatableRef(ActivationID id, RemoteRef ref) |
| { |
| this.id = id; |
| this.ref = ref; |
| } |
| |
| /** |
| * Returns the stub for the remote object whose class is |
| * specified in the activation descriptor. The ActivatableRef |
| * in the resulting stub has its activation id set to the |
| * activation id supplied as the second argument. |
| */ |
| public static Remote getStub(ActivationDesc desc, ActivationID id) |
| throws StubNotFoundException |
| { |
| String className = desc.getClassName(); |
| |
| try { |
| Class<?> cl = |
| RMIClassLoader.loadClass(desc.getLocation(), className); |
| RemoteRef clientRef = new ActivatableRef(id, null); |
| return Util.createProxy(cl, clientRef, false); |
| |
| } catch (IllegalArgumentException e) { |
| throw new StubNotFoundException( |
| "class implements an illegal remote interface", e); |
| |
| } catch (ClassNotFoundException e) { |
| throw new StubNotFoundException("unable to load class: " + |
| className, e); |
| } catch (MalformedURLException e) { |
| throw new StubNotFoundException("malformed URL", e); |
| } |
| } |
| |
| /** |
| * Invoke method on remote object. This method delegates remote |
| * method invocation to the underlying ref type. If the |
| * underlying reference is not known (is null), then the object |
| * must be activated first. If an attempt at method invocation |
| * fails, the object should force reactivation. Method invocation |
| * must preserve "at most once" call semantics. In RMI, "at most |
| * once" applies to parameter deserialization at the remote site |
| * and the remote object's method execution. "At most once" does |
| * not apply to parameter serialization at the client so the |
| * parameters of a call don't need to be buffered in anticipation |
| * of call retry. Thus, a method call is only be retried if the |
| * initial method invocation does not execute at all at the server |
| * (including parameter deserialization). |
| */ |
| public Object invoke(Remote obj, |
| java.lang.reflect.Method method, |
| Object[] params, |
| long opnum) |
| throws Exception |
| { |
| |
| boolean force = false; |
| RemoteRef localRef; |
| Exception exception = null; |
| |
| /* |
| * Attempt object activation if active ref is unknown. |
| * Throws a RemoteException if object can't be activated. |
| */ |
| synchronized (this) { |
| if (ref == null) { |
| localRef = activate(force); |
| force = true; |
| } else { |
| localRef = ref; |
| } |
| } |
| |
| for (int retries = MAX_RETRIES; retries > 0; retries--) { |
| |
| try { |
| return localRef.invoke(obj, method, params, opnum); |
| } catch (NoSuchObjectException e) { |
| /* |
| * Object is not active in VM; retry call |
| */ |
| exception = e; |
| } catch (ConnectException e) { |
| /* |
| * Failure during connection setup; retry call |
| */ |
| exception = e; |
| } catch (UnknownHostException e) { |
| /* |
| * Failure during connection setup; retry call. |
| */ |
| exception = e; |
| } catch (ConnectIOException e) { |
| /* |
| * Failure reusing cached connection; retry call |
| */ |
| exception = e; |
| } catch (MarshalException e) { |
| /* |
| * Failure during parameter serialization; call may |
| * have reached server, so call retry not possible. |
| */ |
| throw e; |
| } catch (ServerError e) { |
| /* |
| * Call reached server; propagate remote exception. |
| */ |
| throw e; |
| } catch (ServerException e) { |
| /* |
| * Call reached server; propagate remote exception |
| */ |
| throw e; |
| } catch (RemoteException e) { |
| /* |
| * This is a catch-all for other RemoteExceptions. |
| * UnmarshalException being the only one relevant. |
| * |
| * StubNotFoundException should never show up because |
| * it is generally thrown when attempting to locate |
| * a stub. |
| * |
| * UnexpectedException should never show up because |
| * it is only thrown by a stub and would be wrapped |
| * in a ServerException if it was propagated by a |
| * remote call. |
| */ |
| synchronized (this) { |
| if (localRef == ref) { |
| ref = null; // this may be overly conservative |
| } |
| } |
| |
| throw e; |
| } |
| |
| if (retries > 1) { |
| /* |
| * Activate object, since object could not be reached. |
| */ |
| synchronized (this) { |
| if (localRef.remoteEquals(ref) || ref == null) { |
| RemoteRef newRef = activate(force); |
| |
| if (newRef.remoteEquals(localRef) && |
| exception instanceof NoSuchObjectException && |
| force == false) { |
| /* |
| * If last exception was NoSuchObjectException, |
| * then old value of ref is definitely wrong, |
| * so make sure that it is different. |
| */ |
| newRef = activate(true); |
| } |
| |
| localRef = newRef; |
| force = true; |
| } else { |
| localRef = ref; |
| force = false; |
| } |
| } |
| } |
| } |
| |
| /* |
| * Retries unsuccessful, so throw last exception |
| */ |
| throw exception; |
| } |
| |
| /** |
| * private method to obtain the ref for a call. |
| */ |
| private synchronized RemoteRef getRef() |
| throws RemoteException |
| { |
| if (ref == null) { |
| ref = activate(false); |
| } |
| |
| return ref; |
| } |
| |
| /** |
| * private method to activate the remote object. |
| * |
| * NOTE: the caller must be synchronized on "this" before |
| * calling this method. |
| */ |
| private RemoteRef activate(boolean force) |
| throws RemoteException |
| { |
| assert Thread.holdsLock(this); |
| |
| ref = null; |
| try { |
| /* |
| * Activate the object and retrieve the remote reference |
| * from inside the stub returned as the result. Then |
| * set this activatable ref's internal ref to be the |
| * ref inside the ref of the stub. In more clear terms, |
| * the stub returned from the activate call contains an |
| * ActivatableRef. We need to set the ref in *this* |
| * ActivatableRef to the ref inside the ActivatableRef |
| * retrieved from the stub. The ref type embedded in the |
| * ActivatableRef is typically a UnicastRef. |
| */ |
| |
| Remote proxy = id.activate(force); |
| ActivatableRef newRef = null; |
| |
| if (proxy instanceof RemoteStub) { |
| newRef = (ActivatableRef) ((RemoteStub) proxy).getRef(); |
| } else { |
| /* |
| * Assume that proxy is an instance of a dynamic proxy |
| * class. If that assumption is not correct, or either of |
| * the casts below fails, the resulting exception will be |
| * wrapped in an ActivateFailedException below. |
| */ |
| RemoteObjectInvocationHandler handler = |
| (RemoteObjectInvocationHandler) |
| Proxy.getInvocationHandler(proxy); |
| newRef = (ActivatableRef) handler.getRef(); |
| } |
| ref = newRef.ref; |
| return ref; |
| |
| } catch (ConnectException e) { |
| throw new ConnectException("activation failed", e); |
| } catch (RemoteException e) { |
| throw new ConnectIOException("activation failed", e); |
| } catch (UnknownObjectException e) { |
| throw new NoSuchObjectException("object not registered"); |
| } catch (ActivationException e) { |
| throw new ActivateFailedException("activation failed", e); |
| } |
| } |
| |
| /** |
| * This call is used by the old 1.1 stub protocol and is |
| * unsupported since activation requires 1.2 stubs. |
| */ |
| public synchronized RemoteCall newCall(RemoteObject obj, |
| Operation[] ops, |
| int opnum, |
| long hash) |
| throws RemoteException |
| { |
| throw new UnsupportedOperationException(versionComplaint); |
| } |
| |
| /** |
| * This call is used by the old 1.1 stub protocol and is |
| * unsupported since activation requires 1.2 stubs. |
| */ |
| public void invoke(RemoteCall call) throws Exception |
| { |
| throw new UnsupportedOperationException(versionComplaint); |
| } |
| |
| /** |
| * This call is used by the old 1.1 stub protocol and is |
| * unsupported since activation requires 1.2 stubs. |
| */ |
| public void done(RemoteCall call) throws RemoteException { |
| throw new UnsupportedOperationException(versionComplaint); |
| } |
| |
| /** |
| * Returns the class of the ref type to be serialized |
| */ |
| public String getRefClass(ObjectOutput out) |
| { |
| return "ActivatableRef"; |
| } |
| |
| /** |
| * Write out external representation for remote ref. |
| */ |
| public void writeExternal(ObjectOutput out) throws IOException |
| { |
| RemoteRef localRef = ref; |
| |
| out.writeObject(id); |
| if (localRef == null) { |
| out.writeUTF(""); |
| } else { |
| out.writeUTF(localRef.getRefClass(out)); |
| localRef.writeExternal(out); |
| } |
| } |
| |
| /** |
| * Read in external representation for remote ref. |
| * @exception ClassNotFoundException If the class for an object |
| * being restored cannot be found. |
| */ |
| public void readExternal(ObjectInput in) |
| throws IOException, ClassNotFoundException |
| { |
| id = (ActivationID)in.readObject(); |
| ref = null; |
| String className = in.readUTF(); |
| |
| if (className.equals("")) return; |
| |
| try { |
| Class<?> refClass = Class.forName(RemoteRef.packagePrefix + "." + |
| className); |
| ref = (RemoteRef)refClass.newInstance(); |
| ref.readExternal(in); |
| } catch (InstantiationException e) { |
| throw new UnmarshalException("Unable to create remote reference", |
| e); |
| } catch (IllegalAccessException e) { |
| throw new UnmarshalException("Illegal access creating remote reference"); |
| } |
| } |
| |
| //----------------------------------------------------------------------; |
| /** |
| * Method from object, forward from RemoteObject |
| */ |
| public String remoteToString() { |
| return Util.getUnqualifiedName(getClass()) + |
| " [remoteRef: " + ref + "]"; |
| } |
| |
| /** |
| * default implementation of hashCode for remote objects |
| */ |
| public int remoteHashCode() { |
| return id.hashCode(); |
| } |
| |
| /** default implementation of equals for remote objects |
| */ |
| public boolean remoteEquals(RemoteRef ref) { |
| if (ref instanceof ActivatableRef) |
| return id.equals(((ActivatableRef)ref).id); |
| return false; |
| } |
| } |