/* | 
|
 * Copyright (c) 1997, 2013, 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 setting up multiplexed connection or 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;  | 
|
}  | 
|
}  |