All Downloads are FREE. Search and download functionalities are using the official Maven repository.

hudson.remoting.RemoteInvocationHandler Maven / Gradle / Ivy

Go to download

Contains the bootstrap code to bridge separate JVMs into a single semi-shared space. Reusable outside Hudson.

There is a newer version: 3.0.3
Show newest version
/*******************************************************************************
 *
 * Copyright (c) 2004-2009 Oracle Corporation.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors: 
*
*    Kohsuke Kawaguchi
 *     
 *
 *******************************************************************************/ 

package hudson.remoting;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * Sits behind a proxy object and implements the proxy logic.
 *
 * @author Kohsuke Kawaguchi
 */
final class RemoteInvocationHandler implements InvocationHandler, Serializable {
    /**
     * This proxy acts as a proxy to the object of
     * Object ID on the remote {@link Channel}.
     */
    private final int oid;

    /**
     * Represents the connection to the remote {@link Channel}.
     *
     * 

* This field is null when a {@link RemoteInvocationHandler} is just * created and not working as a remote proxy. Once tranferred to the * remote system, this field is set to non-null. */ private transient Channel channel; /** * True if we are proxying an user object. */ private final boolean userProxy; /** * If true, this proxy is automatically unexported by the calling {@link Channel}, * so this object won't release the object at {@link #finalize()}. *

* This ugly distinction enables us to keep the # of exported objects low for * the typical situation where the calls are synchronous (thus end of the call * signifies full unexport of all involved objects.) */ private final boolean autoUnexportByCaller; /** * If true, indicates that this proxy object is being sent back * to where it came from. If false, indicates that this proxy * is being sent to the remote peer. * * Only used in the serialized form of this class. */ private boolean goingHome; /** * Creates a proxy that wraps an existing OID on the remote. */ RemoteInvocationHandler(Channel channel, int id, boolean userProxy, boolean autoUnexportByCaller) { this.channel = channel; this.oid = id; this.userProxy = userProxy; this.autoUnexportByCaller = autoUnexportByCaller; } /** * Wraps an OID to the typed wrapper. */ public static T wrap(Channel channel, int id, Class type, boolean userProxy, boolean autoUnexportByCaller) { ClassLoader cl = type.getClassLoader(); // if the type is a JDK-defined type, classloader should be for IReadResolve if(cl==null || cl==ClassLoader.getSystemClassLoader()) cl = IReadResolve.class.getClassLoader(); return type.cast(Proxy.newProxyInstance(cl, new Class[]{type,IReadResolve.class}, new RemoteInvocationHandler(channel,id,userProxy,autoUnexportByCaller))); } /** * If the given object is a proxy to a remote object in the specified channel, * return its object ID. Otherwise return -1. *

* This method can be used to get back the original reference when * a proxy is sent back to the channel it came from. */ public static int unwrap(Object proxy, Channel src) { InvocationHandler h = Proxy.getInvocationHandler(proxy); if (h instanceof RemoteInvocationHandler) { RemoteInvocationHandler rih = (RemoteInvocationHandler) h; if(rih.channel==src) return rih.oid; } return -1; } /** * If the given object is a proxy object, return the {@link Channel} * object that it's associated with. Otherwise null. */ public static Channel unwrap(Object proxy) { InvocationHandler h = Proxy.getInvocationHandler(proxy); if (h instanceof RemoteInvocationHandler) { RemoteInvocationHandler rih = (RemoteInvocationHandler) h; return rih.channel; } return null; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getDeclaringClass()==IReadResolve.class) { // readResolve on the proxy. // if we are going back to where we came from, replace the proxy by the real object if(goingHome) return channel.getExportedObject(oid); else return proxy; } if(channel==null) throw new IllegalStateException("proxy is not connected to a channel"); if(args==null) args = EMPTY_ARRAY; Class dc = method.getDeclaringClass(); if(dc ==Object.class) { // handle equals and hashCode by ourselves try { return method.invoke(this,args); } catch (InvocationTargetException e) { throw e.getTargetException(); } } // delegate the rest of the methods to the remote object if(userProxy) return channel.call(new RPCRequest(oid,method,args,dc.getClassLoader())); else return new RPCRequest(oid,method,args).call(channel); } private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { channel = Channel.current(); ois.defaultReadObject(); } private void writeObject(ObjectOutputStream oos) throws IOException { goingHome = channel!=null; oos.defaultWriteObject(); } /** * Two proxies are the same iff they represent the same remote object. */ public boolean equals(Object o) { if (o == null) { return false; } if(Proxy.isProxyClass(o.getClass())) o = Proxy.getInvocationHandler(o); if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; RemoteInvocationHandler that = (RemoteInvocationHandler) o; return this.oid==that.oid && this.channel==that.channel; } public int hashCode() { return oid; } protected void finalize() throws Throwable { // unexport the remote object if(channel!=null && !autoUnexportByCaller) channel.send(new UnexportCommand(oid)); super.finalize(); } private static final long serialVersionUID = 1L; /** * Executes the method call remotely. * * If used as {@link Request}, this can be used to provide a lower-layer * for the use inside remoting, to implement the classloader delegation, and etc. * The downside of this is that the classes used as a parameter/return value * must be available to both JVMs. * * If used as {@link Callable} in conjunction with {@link UserRequest}, * this can be used to send a method call to user-level objects, and * classes for the parameters and the return value are sent remotely if needed. */ static final class RPCRequest extends Request implements DelegatingCallable { /** * Target object id to invoke. */ private final int oid; private final String methodName; /** * Type name of the arguments to invoke. They are names because * neither {@link Method} nor {@link Class} is serializable. */ private final String[] types; /** * Arguments to invoke the method with. */ private final Object[] arguments; /** * If this is used as {@link Callable}, we need to remember what classloader * to be used to serialize the request and the response. */ private transient ClassLoader classLoader; public RPCRequest(int oid, Method m, Object[] arguments) { this(oid,m,arguments,null); } public RPCRequest(int oid, Method m, Object[] arguments, ClassLoader cl) { this.oid = oid; this.arguments = arguments; this.methodName = m.getName(); this.classLoader = cl; this.types = new String[arguments.length]; Class[] params = m.getParameterTypes(); for( int i=0; i[] paramTypes = m.getParameterTypes(); if (types.length != paramTypes.length) { continue; } for( int i=0; i





© 2015 - 2024 Weber Informatics LLC | Privacy Policy