/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.jini.jeri;

import com.sun.jini.action.GetBooleanAction;
import com.sun.jini.jeri.internal.runtime.Util;
import com.sun.jini.jeri.internal.runtime.WeakKey;
import com.sun.jini.logging.Levels;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.lang.ref.ReferenceQueue;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.ServerError;
import java.rmi.ServerException;
import java.rmi.UnmarshalException;
import java.rmi.server.ExportException;
import java.rmi.server.ServerNotActiveException;
import java.security.AccessControlException;
import java.security.AccessController;
import java.security.CodeSource;
import java.security.Permission;
import java.security.Principal;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import javax.security.auth.Subject;
import net.jini.core.constraint.Integrity;
import net.jini.core.constraint.InvocationConstraint;
import net.jini.core.constraint.InvocationConstraints;
import net.jini.core.constraint.MethodConstraints;
import net.jini.export.ServerContext;
import net.jini.io.MarshalInputStream;
import net.jini.io.MarshalOutputStream;
import net.jini.io.UnsupportedConstraintException;
import net.jini.io.context.ClientSubject;
import net.jini.security.AccessPermission;
import net.jini.security.proxytrust.ProxyTrust;
import net.jini.security.proxytrust.ProxyTrustVerifier;
import net.jini.security.proxytrust.ServerProxyTrust;

/**
 * A basic implementation of the {@link InvocationDispatcher} interface,
 * providing preinvocation access control for
 * remote objects exported using {@link BasicJeriExporter}.
 *
 * <p>This invocation dispatcher handles incoming remote method invocations
 * initiated by proxies using {@link BasicInvocationHandler}, and expects
 * that a dispatched request, encapsulated in the {@link InboundRequest}
 * object passed to the {@link #dispatch dispatch} method, was sent using
 * the protocol implemented by <code>BasicInvocationHandler</code>.
 *
 * <p>A basic permission-based preinvocation access control mechanism is
 * provided. A permission class can be specified when an invocation
 * dispatcher is constructed; instances of that class are constructed using
 * either a {@link Method} instance or a <code>String</code> representing
 * the remote method being invoked. The class can have a constructor with a
 * <code>Method</code> parameter to permit an arbitrary mapping to the
 * actual permission target name and actions; otherwise, the class must
 * have a constructor taking the fully qualified name of the remote method
 * as a <code>String</code>. For each incoming call on a remote object, the
 * client subject must be granted the associated permission for that remote
 * method.  (Access control for an individual remote method can effectively
 * be disabled by granting the associated permission to all protection
 * domains.) A simple subclass of {@link AccessPermission} is typically
 * used as the permission class.
 *
 * <p>Other access control mechanisms can be implemented by subclassing this
 * class and overriding the various protected methods.
 * 
 * <p>This class is designed to support dispatching remote calls to the
 * {@link ProxyTrust#getProxyVerifier ProxyTrust.getProxyVerifier} method
 * to the local {@link ServerProxyTrust#getProxyVerifier
 * ServerProxyTrust.getProxyVerifier} method of a remote object, to allow a
 * remote object to be exported in such a way that its proxy can be
 * directly trusted by clients as well as in such a way that its proxy can
 * be trusted by clients using {@link ProxyTrustVerifier}.
 *
 * @author	Sun Microsystems, Inc.
 * @see		BasicInvocationHandler
 * @since 2.0
 *
 * @com.sun.jini.impl
 *
 * This implementation uses the following system property:
 * <dl>
 * <dt><code>com.sun.jini.jeri.server.suppressStackTrace</code>
 * <dd>If <code>true</code>, removes server-side stack traces before
 * marshalling an exception thrown as a result of a remote call.  The
 * default value is <code>false</code>.
 * </dl>
 * 
 * <p>This implementation uses the {@link Logger} named
 * <code>net.jini.jeri.BasicInvocationDispatcher</code> to log
 * information at the following levels:
 *
 * <table summary="Describes what is logged by BasicInvocationDispatcher at
 *        various logging levels" border=1 cellpadding=5>
 *
 * <tr> <th> Level <th> Description
 *
 * <tr> <td> {@link Levels#FAILED FAILED} <td> exception that caused a request
 * to be aborted 
 *
 * <tr> <td> {@link Levels#FAILED FAILED} <td> exceptional result of a
 * remote call 
 *
 * <tr> <td> {@link Level#FINE FINE} <td> incoming remote call
 * 
 * <tr> <td> {@link Level#FINE FINE} <td> successful return of remote call
 *
 * <tr> <td> {@link Level#FINEST FINEST} <td> more detailed information on
 * the above (for example, actual argument and return values)
 *
 * </table>
 **/
public class BasicInvocationDispatcher implements InvocationDispatcher {

    /** Marshal stream protocol version. */
    static final byte VERSION = 0x0;
    
    /** Marshal stream protocol version mismatch. */
    static final byte MISMATCH = 0x0;
    
    /** Normal return (with or without return value). */
    static final byte RETURN = 0x01;
    
    /** Exceptional return. */
    static final byte THROW = 0x02;

    /** The class loader used by createMarshalInputStream */
    private final ClassLoader loader;
    
    /** The server constraints. */
    private final MethodConstraints serverConstraints;
    
    /**
     * Constructor for the Permission class, that has either one String
     * or one Method parameter, or null.
     */
    private final Constructor permConstructor;
    
    /** True if permConstructor has a Method parameter. */
    private final boolean permUsesMethod;
    
    /** Map from Method to Permission. */
    private final Map permissions;
    
    /** Map from Long method hash to Method, for all remote methods. */
    private final Map methods;

    /** Map from WeakKey(Subject) to ProtectionDomain. */
    private static final Map domains = new HashMap();
    
    /** Reference queue for the weak keys in the domains map. */
    private static final ReferenceQueue queue = new ReferenceQueue();

    /** dispatch logger */
    private static final Logger logger =
	Logger.getLogger("net.jini.jeri.BasicInvocationDispatcher");

    /**
     * Flag to remove server-side stack traces before marshalling
     * exceptions thrown by remote invocations to this VM
     */
    private static final boolean suppressStackTraces =
	((Boolean) AccessController.doPrivileged(new GetBooleanAction(
	    "com.sun.jini.jeri.server.suppressStackTraces")))
	    .booleanValue();

    /** Empty codesource. */
    private static final CodeSource emptyCS =
	new CodeSource(null, (Certificate[]) null);
    
    /** ProtectionDomain containing the empty codesource. */
    private static final ProtectionDomain emptyPD =
	new ProtectionDomain(emptyCS, null, null, null);

    /** Cached getClassLoader permission */
    private static final Permission getClassLoaderPermission =
	new RuntimePermission("getClassLoader");
    
    /**
     * Creates an invocation dispatcher to receive incoming remote calls
     * for the specified methods, for a server and transport with the
     * specified capabilities, enforcing the specified constraints,
     * performing preinvocation access control using the specified
     * permission class (if any).  The specified class loader is used by
     * the {@link #createMarshalInputStream createMarshalInputStream}
     * method.
     *
     * <p>For each combination of constraints that might need to be
     * enforced (obtained by calling the {@link
     * MethodConstraints#possibleConstraints possibleConstraints} method on
     * the specified server constraints, or using an empty constraints
     * instance if the specified server constraints instance is
     * <code>null</code>), calling the {@link
     * ServerCapabilities#checkConstraints checkConstraints} method of the
     * specified capabilities object with those constraints must return
     * constraints containing at most an {@link Integrity} constraint as a
     * requirement, or an <code>ExportException</code> is thrown.
     *
     * @param	methods a collection of {@link Method} instances for the
     *		remote methods
     * @param	serverCapabilities the transport capabilities of the server
     * @param	serverConstraints the server constraints, or <code>null</code>
     * @param	permissionClass the permission class, or <code>null</code>
     * @param	loader the class loader, or <code>null</code>
     *
     * @throws	SecurityException if the permission class is not
     *		<code>null</code> and is in a named package and a
     *		security manager exists and invoking its
     *		<code>checkPackageAccess</code> method with the package
     *		name of the permission class throws a
     *		<code>SecurityException</code>
     * @throws	IllegalArgumentException if the permission class
     *		is abstract, is not <code>public</code>, is not a subclass
     *		of {@link Permission}, or does not have a public
     *		constructor that has either one <code>String</code>
     *		parameter or one {@link Method} parameter and has no
     *		declared exceptions, or if any element of
     *		<code>methods</code> is not a {@link Method} instance
     * @throws	NullPointerException if <code>methods</code> or
     *		<code>serverCapabilities</code> is <code>null</code>, or if
     *		<code>methods</code> contains a <code>null</code> element
     * @throws	ExportException if any of the possible server constraints
     * 		cannot be satisfied according to the specified server
     *		capabilities 
     **/
    public BasicInvocationDispatcher(Collection methods,
				     ServerCapabilities serverCapabilities,
				     MethodConstraints serverConstraints,
				     Class permissionClass,
				     ClassLoader loader)
	throws ExportException
    {
	if (serverCapabilities == null) {
	    throw new NullPointerException();
	}
	this.methods = new HashMap();
	this.loader = loader;
	for (Iterator iter = methods.iterator(); iter.hasNext(); ) {
	    Object m = iter.next();
	    if (m == null) {
		throw new NullPointerException("methods contains null");
	    } else if (!(m instanceof Method)) {
		throw new IllegalArgumentException(
		    "methods must contain only Methods");
	    }
	    this.methods.put(new Long(Util.getMethodHash((Method) m)), m);
	}
	this.serverConstraints = serverConstraints;
	if (permissionClass != null) {
	    Util.checkPackageAccess(permissionClass);
	}
	permConstructor = getConstructor(permissionClass);
	
	permUsesMethod =
	    (permConstructor != null &&
	     permConstructor.getParameterTypes()[0] == Method.class);
	permissions = (permConstructor == null ?
		       null :
		       new IdentityHashMap(methods.size() + 2));
	try {
	    if (serverConstraints == null) {
		checkConstraints(serverCapabilities,
				 InvocationConstraints.EMPTY);
	    } else {
		Iterator iter = serverConstraints.possibleConstraints();
		while (iter.hasNext()) {
		    checkConstraints(serverCapabilities,
				     (InvocationConstraints) iter.next());
		}
	    }
	} catch (UnsupportedConstraintException e) {
	    throw new ExportException(
		"server does not support some constraints", e);
	}
    }

    /**
     * Check that the only unfulfilled requirement is Integrity.
     */
    private static void checkConstraints(ServerCapabilities serverCapabilities,
					 InvocationConstraints constraints)
	throws UnsupportedConstraintException
    {
	InvocationConstraints unfulfilled =
	    serverCapabilities.checkConstraints(constraints);
	for (Iterator i = unfulfilled.requirements().iterator(); i.hasNext();)
	{
	    InvocationConstraint c = (InvocationConstraint) i.next();
	    if (!(c instanceof Integrity)) {
		throw new UnsupportedConstraintException(
		    "cannot satisfy unfulfilled constraint: " + c);
	    }
	    // REMIND: support ConstraintAlternatives containing Integrity?
	}
    }

    /**
     * Returns the class loader specified during construction.
     *
     * @return the class loader
     */
    protected final ClassLoader getClassLoader() {
	return loader;
    }
    
    /**
     * Checks that the specified class is a valid permission class for use in
     * preinvocation access control.
     *
     * @param	permissionClass the permission class, or <code>null</code>
     * @throws IllegalArgumentException if the permission class is abstract,
     * is not a subclass of {@link Permission}, or does not have a public
     * constructor that has either one <code>String</code> parameter or one
     * {@link Method} parameter and has no declared exceptions
     **/
    public static void checkPermissionClass(Class permissionClass) {
	getConstructor(permissionClass);
    }

    /**
     * Checks that the specified class is a subclass of Permission, is
     * public, is not abstract, and has the right one-parameter Method or
     * String constructor, and returns that constructor, otherwise throws
     * IllegalArgumentException.
     **/
    private static Constructor getConstructor(Class permissionClass) {
	if (permissionClass == null) {
	    return null;
	} else {
	    int mods = permissionClass.getModifiers();
	    if (!Permission.class.isAssignableFrom(permissionClass) ||
		Modifier.isAbstract(mods) || !Modifier.isPublic(mods))
	    {
		throw new IllegalArgumentException("bad permission class");
	    }
	}
	try {
	    Constructor permConstructor =
		permissionClass.getConstructor(new Class[]{Method.class});
	    if (permConstructor.getExceptionTypes().length == 0) {
		return permConstructor;
	    }
	} catch (NoSuchMethodException e) {
	}
	try {
	    Constructor permConstructor =
		permissionClass.getConstructor(new Class[]{String.class});
	    if (permConstructor.getExceptionTypes().length == 0) {
		return permConstructor;
	    }
	} catch (NoSuchMethodException ee) {
	}
	throw new IllegalArgumentException("bad permission class");
    }

    /**
     * Dispatches the specified inbound request to the specified remote object.
     * When used in conjunction with {@link BasicJeriExporter}, this
     * method is called in a context that has the security context and
     * context class loader specified by
     * {@link BasicJeriExporter#export BasicJeriExporter.export}.
     * 
     * <p><code>BasicInvocationDispatcher</code> implements this method to
     * execute the following actions in order:
     *
     * <ul>
     * <li>A byte specifying the marshal stream protocol version is read
     * from the request input stream of the inbound request. If any
     * exception is thrown when reading this byte, the inbound request is
     * aborted and this method returns. If the byte is not
     * <code>0x00</code>, two byte values of <code>0x00</code> (indicating
     * a marshal stream protocol version mismatch) are written to the
     * response output stream of the inbound request, the output stream is
     * closed, and this method returns.
     * 
     * <li>If the version byte is <code>0x00</code>, a second byte
     * specifying object integrity is read from the same stream.  If any
     * exception is thrown when reading this byte, the inbound request is
     * aborted and this method returns.  Object integrity will be enforced
     * if the value read is not <code>0x00</code>, but will not be enforced
     * if the value is <code>0x00</code>. An {@link
     * net.jini.io.context.IntegrityEnforcement} element is then added to
     * the server context, reflecting whether or not object integrity is
     * being enforced.
     *
     * <li>The {@link #createMarshalInputStream createMarshalInputStream}
     * method of this invocation dispatcher is called, passing the remote
     * object, the inbound request, a boolean indicating if object
     * integrity is being enforced, and the server context, to create the
     * marshal input stream for unmarshalling the request.
     *
     * <li>The {@link #unmarshalMethod unmarshalMethod} of this
     * invocation dispatcher is called with the remote object, the marshal
     * input stream, and the server context to obtain the remote method.
     *
     * <li> The {@link InboundRequest#checkConstraints checkConstraints}
     * method of the inbound request is called with the constraints that
     * must be enforced for that remote method, obtained by passing the
     * remote method to the {@link MethodConstraints#getConstraints
     * getConstraints} method of this invocation dispatcher's server
     * constraints, and adding {@link Integrity#YES Integrity.YES} as a
     * requirement if object integrity is being enforced. If the
     * unfulfilled requirements returned by <code>checkConstraints</code>
     * contains a constraint that is not an instance of {@link Integrity}
     * or if integrity is not being enforced and the returned requirements
     * contains the element <code>Integrity.YES</code>, an
     * <code>UnsupportedConstraintException</code> is sent back to the
     * caller as described further below. Otherwise, the {@link
     * #checkAccess checkAccess} method of this invocation dispatcher is
     * called with the remote object, the remote method, the enforced
     * constraints, and the server context.
     *
     * <li>The method arguments are obtained by calling the {@link
     * #unmarshalArguments unmarshalArguments} method of this invocation
     * dispatcher with the remote object, the remote method, the marshal
     * input stream, and the server context.
     * 
     * <li>If any exception is thrown during this unmarshalling, that exception
     * is sent back to the caller as described further below; however, if the
     * exception is a checked exception ({@link IOException},
     * {@link ClassNotFoundException}, or {@link NoSuchMethodException}), the
     * exception is first wrapped in an {@link UnmarshalException} and the
     * wrapped exception is sent back.
     *
     * <li>Otherwise, if unmarshalling is successful, the {@link #invoke
     * invoke} method of this invocation dispatcher is then called with the
     * remote object, the remote method, the arguments returned by
     * <code>unmarshalArguments</code>, and the server context. If
     * <code>invoke</code> throws an exception, that exception is sent back
     * to the caller as described further below.
     *
     * <li>The input stream is closed whether or not an exception was
     * thrown unmarshalling the arguments or invoking the method.
     *
     * <li>If <code>invoke</code> returns normally, a byte value of
     * <code>0x01</code> is written to the response output stream of the
     * inbound request. Then the {@link #createMarshalOutputStream
     * createMarshalOutputStream} method of this invocation dispatcher is
     * called, passing the remote object, the remote method, the inbound
     * request, and the server context, to create the marshal output stream
     * for marshalling the response. Then the {@link #marshalReturn
     * marshalReturn} method of this invocation dispatcher is called with
     * the remote object, the remote method, the value returned by
     * <code>invoke</code>, the marshal output stream, and the server
     * context. Then the marshal output stream is closed. Any exception
     * thrown during this marshalling is ignored.
     * 
     * <li>When an exception is sent back to the caller, a byte value of
     * <code>0x02</code> is written to the response output stream of the
     * inbound request. Then a marshal output stream is created by calling
     * the <code>createMarshalOutputStream</code> method as described above
     * (but with a <code>null</code> remote method if one was not
     * successfully unmarshalled). Then the {@link #marshalThrow
     * marshalThrow} method of this invocation dispatcher is called with
     * the remote object, the remote method (or <code>null</code> if one
     * was not successfully unmarshalled), the exception, the marshal
     * output stream, and the server context. Then the marshal output
     * stream is closed. Any exception thrown during this marshalling is
     * ignored. If the exception being sent back is a
     * <code>RemoteException</code>, it is wrapped in a {@link
     * ServerException} and the wrapped exception is passed to
     * <code>marshalThrow</code>. If the exception being sent back is an
     * <code>Error</code>, it is wrapped in a {@link ServerError} and the
     * wrapped exception is passed to <code>marshalThrow</code>. If the
     * exception being sent back occurred before or during the call to
     * <code>unmarshalMethod</code>, then the remote method passed to
     * <code>marshalThrow</code> is <code>null</code>.
     * </ul>
     *
     * @throws	NullPointerException {@inheritDoc}
     **/
    public void dispatch(Remote impl,
			 InboundRequest request,
			 Collection context)
    {
	if (impl == null || context == null) {
	    throw new NullPointerException();
	}

	/*
	 * Read (and check) version number and integrity flag.
	 */
	InputStream rin = null;
	boolean integrity;
	try {
	    rin = request.getRequestInputStream();
	    switch (rin.read()) {
		case VERSION:
		    break;
		case -1:
		    throw new EOFException();
		default:
		    rin.close();
		    OutputStream ros = request.getResponseOutputStream();
		    ros.write(MISMATCH);
		    ros.write(VERSION);
		    ros.close();
		    return;
	    }
	    switch (rin.read()) {
	    case 0:
		integrity = false;
		break;
	    case -1:
		throw new EOFException();
	    default:
		integrity = true;
	    }
	} catch (Throwable t) {
	    if (logger.isLoggable(Levels.FAILED)) {
		logThrow(impl, t);
	    }
	    request.abort();
	    return;
	}

	Method method = null;
	Object returnValue= null;
	Throwable t = null;
	boolean fromImpl = false;
	Util.populateContext(context, integrity);
	ObjectInputStream in = null;
	
	try {
	    /*
	     * Unmarshal method and check security constraints.
	     */
	    in = createMarshalInputStream(impl, request, integrity, context);
	    method = unmarshalMethod(impl, in, context);
	    InvocationConstraints sc =
		(serverConstraints == null ?
		 InvocationConstraints.EMPTY :
		 serverConstraints.getConstraints(method));
	    if (integrity && !sc.requirements().contains(Integrity.YES)) {
		Collection requirements = new ArrayList(sc.requirements());
		requirements.add(Integrity.YES);
		sc = new InvocationConstraints(requirements, sc.preferences());
	    }
	    
	    InvocationConstraints unfulfilled = request.checkConstraints(sc);
	    for (Iterator i = unfulfilled.requirements().iterator();
		 i.hasNext();)
	    {
		InvocationConstraint c = (InvocationConstraint) i.next();
		if (!(c instanceof Integrity) ||
		    (!integrity && c == Integrity.YES))
		{
		    throw new UnsupportedConstraintException(
		        "cannot satisfy unfulfilled constraint: " + c);
		}
		// REMIND: support ConstraintAlternatives containing Integrity?
	    }
	    
	    checkAccess(impl, method, sc, context);

	    /*
	     * Unmarshal arguments.
	     */
	    Object[] args = unmarshalArguments(impl, method, in, context);
	    if (logger.isLoggable(Level.FINE)) {
		logCall(impl, method, args);
	    }
	
	    /*
	     * Invoke method on remote object.
	     */
	    try {
		returnValue = invoke(impl, method, args, context);
		if (logger.isLoggable(Level.FINE)) {
		    logReturn(impl, method, returnValue);
		}
	    } catch (Throwable tt) {
		t = tt;
		fromImpl = true;
	    }
	} catch (RuntimeException e) {
	    t = e;
	} catch (Exception e) {
	    t = new UnmarshalException("unmarshalling method/arguments", e);
	} catch (Throwable tt) {
	    t = tt;
	} finally {
	    if (in != null) {
		try {
		    in.close();
		} catch (IOException ignore) {
		}
	    }
	}

	/*
	 * Marshal return value or exception.
	 */
	try {
	    request.getResponseOutputStream().write(t == null ?
						    RETURN : THROW);
	    ObjectOutputStream out =
		createMarshalOutputStream(impl, method, request, context);
	    if (t != null) {
		if (logger.isLoggable(Levels.FAILED)) {
		    logThrow(impl, method, t, fromImpl);
		}
		if (t instanceof RemoteException) {
		    t = new ServerException("RemoteException in server thread",
					    (Exception) t);
		} else if (t instanceof Error) {
		    t = new ServerError("Error in server thread", (Error) t);
		}
		if (suppressStackTraces) {
		    Util.clearStackTraces(t);
		}
		marshalThrow(impl, method, t, out, context);
	    } else {
		marshalReturn(impl, method, returnValue, out, context);
	    }
	    out.close();
	    
	} catch (Throwable tt) {
	    /*
	     * All exceptions are fatal at this point.  There is no
	     * recovery if a problem occurs writing the result, so
	     * abort the call and return.  But first try to close the
	     * response output stream, in case the IOException was
	     * able to be serialized for the client successfully.
	     */
	    try {
		request.getResponseOutputStream().close();
	    } catch (IOException ignore) {
	    }
	    request.abort();
	    if (logger.isLoggable(Levels.FAILED)) {
		logThrow(impl, tt);
	    }
	}
    }

    /**
     * Returns a new marshal input stream to use to read objects from the
     * request input stream obtained by invoking the {@link
     * InboundRequest#getRequestInputStream getRequestInputStream} method
     * on the given <code>request</code>.
     *
     * <p><code>BasicInvocationDispatcher</code> implements this method as
     * follows:
     *
     * <p>First, a class loader is selected to use as the
     * <code>defaultLoader</code> and the <code>verifierLoader</code> for
     * the marshal input stream instance.  If the class loader specified at
     * construction is not <code>null</code>, the selected loader is that
     * loader.  Otherwise, if a security manager exists, its {@link
     * SecurityManager#checkPermission checkPermission} method is invoked
     * with the permission <code>{@link
     * RuntimePermission}("getClassLoader")</code>; this invocation may
     * throw a <code>SecurityException</code>.  If the above security check
     * succeeds, the selected loader is the class loader of
     * <code>impl</code>'s class.
     *
     * <p>This method returns a new {@link MarshalInputStream} instance
     * constructed with the input stream (obtained from the
     * <code>request</code> as specified above) for the input stream
     * <code>in</code>, the selected loader for <code>defaultLoader</code>
     * and <code>verifierLoader</code>, the boolean <code>integrity</code>
     * for <code>verifyCodebaseIntegrity</code>, and an unmodifiable view
     * of <code>context</code> for the <code>context</code> collection.
     * The {@link MarshalInputStream#useCodebaseAnnotations
     * useCodebaseAnnotations} method is invoked on the created stream
     * before it is returned.
     *
     * <p>A subclass can override this method to control how the marshal input
     * stream is created or implemented.
     *
     * @param	impl the remote object
     * @param	request the inbound request
     * @param	integrity <code>true</code> if object integrity is being
     * 		enforced for the remote call, and <code>false</code> otherwise
     * @param	context the server context
     * @return	a new marshal input stream for unmarshalling a call request
     * @throws	IOException if an I/O exception occurs
     * @throws	NullPointerException if any argument is <code>null</code>
     **/
    protected ObjectInputStream
        createMarshalInputStream(Object impl,
				 InboundRequest request,
				 boolean integrity,
				 Collection context)
	throws IOException
    {
	ClassLoader streamLoader;
	if (loader != null) {
	    streamLoader = getClassLoader();
	} else {
	    SecurityManager security = System.getSecurityManager();
	    if (security != null) {
		security.checkPermission(getClassLoaderPermission);
	    }
	    streamLoader = impl.getClass().getClassLoader();
	}
	
	Collection unmodContext = Collections.unmodifiableCollection(context);
	MarshalInputStream in =
	    new MarshalInputStream(request.getRequestInputStream(),
				   streamLoader, integrity,
				   streamLoader, unmodContext);
	in.useCodebaseAnnotations();
	return in;
    }
    
    /**
     * Returns a new marshal output stream to use to write objects to the
     * response output stream obtained by invoking the {@link
     * InboundRequest#getResponseOutputStream getResponseOutputStream}
     * method on the given <code>request</code>.
     *
     * <p>This method will be called with a <code>null</code>
     * <code>method</code> argument if an <code>IOException</code> occurred
     * when reading method information from the incoming call stream.
     *
     * <p><code>BasicInvocationDispatcher</code> implements this method to
     * return a new {@link MarshalOutputStream} instance constructed with
     * the output stream obtained from the <code>request</code> as
     * specified above and an unmodifiable view of the given
     * <code>context</code> collection.
     *
     * <p>A subclass can override this method to control how the marshal output
     * stream is created or implemented.
     *
     * @param	impl the remote object
     * @param   method the possibly-<code>null</code> <code>Method</code>
     *		instance corresponding to the interface method invoked on
     *		the remote object
     * @param	request the inbound request
     * @param	context the server context
     * @return	a new marshal output stream for marshalling a call response
     * @throws	IOException if an I/O exception occurs
     * @throws	NullPointerException if <code>impl</code>,
     *		<code>request</code>, or <code>context</code> is
     *		<code>null</code>
     **/
    protected ObjectOutputStream
        createMarshalOutputStream(Object impl,
				  Method method,
				  InboundRequest request,
				  Collection context)
	throws IOException
    {
	if (impl == null) {
	    throw new NullPointerException();
	}
	OutputStream out = request.getResponseOutputStream();
	Collection unmodContext = Collections.unmodifiableCollection(context);
	return new MarshalOutputStream(out, unmodContext);
    }
							  
    /**
     * Checks that the client has permission to invoke the specified method on
     * the specified remote object.
     *
     * <p><code>BasicInvocationDispatcher</code> implements this method as
     * follows:
     *
     * <p>If a permission class was specified when this invocation
     * dispatcher was constructed, {@link #checkClientPermission
     * checkClientPermission} is called with a permission constructed from
     * the permission class. If the permission class has a constructor with
     * a <code>Method</code> parameter, the permission is constructed by
     * passing the specified method to that constructor. Otherwise the
     * permission is constructed by passing the fully qualified name of the
     * method to the constructor with a <code>String</code> parameter,
     * where the argument is formed by concatenating the name of the
     * declaring class of the specified method and the name of the method,
     * separated by ".".
     *
     * <p>A subclass can override this method to implement other preinvocation
     * access control mechanisms.
     *
     * @param	impl the remote object
     * @param	method the remote method
     * @param	constraints the enforced constraints for the specified
     *		method, or <code>null</code>
     * @param	context the server context
     * @throws	SecurityException if the current client subject does not
     *		have permission to invoke the method
     * @throws	IllegalStateException if the current thread is not executing an
     *		incoming remote call for a remote object
     * @throws	NullPointerException if <code>impl</code>,
     *		<code>method</code>, or <code>context</code> is
     *		<code>null</code> 
     **/
    protected void checkAccess(Remote impl,
			       Method method,
			       InvocationConstraints constraints,
			       Collection context)
    {
	if (impl == null || method == null || context == null) {
	    throw new NullPointerException();
	}
	if (permConstructor == null) {
	    return;
	}
	Permission perm;
	synchronized (permissions) {
	    perm = (Permission) permissions.get(method);
	}
	if (perm == null) {
	    try {
		perm = (Permission) permConstructor.newInstance(new Object[]{
		    permUsesMethod ?
			(Object) method :
			method.getDeclaringClass().getName() + "." +
			method.getName()});
	    } catch (InvocationTargetException e) {
		Throwable t = e.getTargetException();
		if (t instanceof Error) {
		    throw (Error) t;
		}
		throw (RuntimeException) t;
	    } catch (Exception e) {
		throw new RuntimeException("unexpected exception", e);
	    }
	    synchronized (permissions) {
		permissions.put(method, perm);
	    }
	}
	checkClientPermission(perm);
    }
    
    /**
     * Checks that the client subject for the current remote call has the
     * specified permission. The client subject is obtained by calling {@link
     * ServerContext#getServerContextElement
     * ServerContext.getServerContextElement}, passing the class {@link
     * ClientSubject}, and then calling the {@link
     * ClientSubject#getClientSubject getClientSubject} method of the returned
     * element (if any). If a security manager is installed, a {@link
     * ProtectionDomain} is constructed with an empty {@link CodeSource}
     * (<code>null</code> location and certificates), <code>null</code>
     * permissions, <code>null</code> class loader, and the principals from
     * the client subject (if any), and the <code>implies</code> method of
     * that protection domain is invoked with the specified permission. If
     * <code>true</code> is returned, this method returns normally, otherwise
     * a <code>SecurityException</code> is thrown. If no security
     * manager is installed, this method returns normally.
     *
     * <p>Note that the permission grant required to satisfy this check must
     * be to the client's principals alone (or a subset thereof); it cannot be
     * qualified by what code is being executed. At the point in a remote call
     * where this method is intended to be used, the useful "call stack" only
     * exists at the other end of the remote call (on the client side), and so
     * cannot meaningfully enter into the access control decision.
     *
     * @param	permission the requested permission
     * @throws	SecurityException if the current client subject has not 
     *		been granted the specified permission
     * @throws	IllegalStateException if the current thread is not executing
     *		an incoming remote method for a remote object
     * @throws	NullPointerException if <code>permission</code> is
     *		<code>null</code> 
     **/
    public static void checkClientPermission(final Permission permission) {
	if (permission == null) {
	    throw new NullPointerException();
	}
	Subject client =
	    (Subject) AccessController.doPrivileged(new PrivilegedAction() {
		public Object run() {
		    try {
			return Util.getClientSubject();
		    } catch (ServerNotActiveException e) {
			throw new IllegalStateException("server not active");
		    }
		}
	    });
	if (System.getSecurityManager() == null) {
	    return;
	}
	ProtectionDomain pd;
	if (client == null) {
	    pd = emptyPD;
	} else {
	    synchronized (domains) {
		WeakKey k;
		while ((k = (WeakKey) queue.poll()) != null) {
		    domains.remove(k);
		}
		pd = (ProtectionDomain) domains.get(new WeakKey(client));
		if (pd == null) {
		    Set set = client.getPrincipals();
		    Principal[] prins =
			(Principal[]) set.toArray(new Principal[set.size()]);
		    pd = new ProtectionDomain(emptyCS, null, null, prins);
		    domains.put(new WeakKey(client, queue), pd);
		}
	    }
	}
	boolean ok = pd.implies(permission);
	// XXX what about logging
	if (!ok) {
	    throw new AccessControlException("access denied " + permission);
	}
    }

    /**
     * Unmarshals a method representation from the marshal input stream,
     * <code>in</code>, and returns the <code>Method</code> object
     * corresponding to that representation.  For each remote call, the
     * <code>dispatch</code> method calls this method to unmarshal the
     * method representation.
     *
     * <p><code>BasicInvocationDispatcher</code> implements this method to
     * call the <code>readLong</code> method on the marshal input stream to
     * read the method's representation encoded as a JRMP method hash
     * (defined in section 8.3 of the Java(TM) Remote Method Invocation
     * (Java RMI) specification) and return its
     * corresponding <code>Method</code> object chosen from the collection
     * of methods passed to the constructor of this invocation dispatcher.
     * If more than one method has the same hash, it is arbitrary as to
     * which one is returned.
     *
     * <p>A subclass can override this method to control how the remote
     * method is unmarshalled.
     *
     * @param	impl the remote object
     * @param	in the marshal input stream for the remote call
     * @param	context the server context passed to the {@link #dispatch
     *		dispatch} method for the remote call being processed
     * @return	a <code>Method</code> object corresponding to the method
     *		representation
     * @throws	IOException if an I/O exception occurs 
     * @throws	NoSuchMethodException if the method representation does not
     * 		correspond to a valid method
     * @throws  ClassNotFoundException if a class could not be found during
     *          unmarshalling
     * @throws	NullPointerException if any argument is <code>null</code>
     **/
    protected Method unmarshalMethod(Remote impl,
				     ObjectInputStream in,
				     Collection context)
        throws IOException, NoSuchMethodException, ClassNotFoundException
    {
	if (impl == null || context == null) {
	    throw new NullPointerException();
	}
	long hash = in.readLong();
	Method method = (Method) methods.get(new Long(hash));
	if (method == null) {
	    throw new NoSuchMethodException(
	     "unrecognized method hash: method not supported by remote object");
	}
	return method;
    }

    /**
     * Unmarshals the arguments for the specified remote <code>method</code>
     * from the specified marshal input stream, <code>in</code>, and returns an
     * <code>Object</code> array containing the arguments read.  For each
     * remote call, the <code>dispatch</code> method calls this method to
     * unmarshal arguments.
     *
     * <p><code>BasicInvocationDispatcher</code> implements this method to
     * unmarshal each argument as follows:
     *
     * <p>If the corresponding declared parameter type is primitive, then
     * the primitive value is read from the stream using the
     * corresponding <code>read</code> method for that primitive type (for
     * example, if the type is <code>int.class</code>, then the primitive
     * <code>int</code> value is read to the stream using the
     * <code>readInt</code> method) and the value is wrapped in the
     * corresponding primitive wrapper class for that type (e.g.,
     * <code>Integer</code> for <code>int</code>, etc.).  Otherwise, the
     * argument is read from the stream using the <code>readObject</code>
     * method and returned as is.
     *
     * <p>A subclass can override this method to unmarshal the arguments in an
     * alternative context, perform post-processing on the arguments,
     * unmarshal additional implicit data, or otherwise control how the
     * arguments are unmarshalled. In general, the context used should mirror
     * the context in which the arguments are manipulated in the
     * implementation of the remote object.
     *
     * @param	impl the remote object
     * @param   method the <code>Method</code> instance corresponding
     *          to the interface method invoked on the remote object
     * @param	in the incoming request stream for the remote call
     * @param	context the server context passed to the {@link #dispatch
     *		dispatch} method for the remote call being processed
     * @return	an <code>Object</code> array containing
     *		the unmarshalled arguments.  If an argument's corresponding
     *		declared parameter type is primitive, then its value is
     *		represented with an instance of the corresponding primitive
     *		wrapper class; otherwise, the value for that argument is an
     *		object of a class assignable to the declared parameter type.
     * @throws	IOException if an I/O exception occurs
     * @throws  ClassNotFoundException if a class could not be found during
     *          unmarshalling
     * @throws	NullPointerException if any argument is <code>null</code>
     **/
    protected Object[] unmarshalArguments(Remote impl,
					  Method method,
					  ObjectInputStream in,
					  Collection context)
	throws IOException, ClassNotFoundException
    {
	if (impl == null || in == null || context == null) {
	    throw new NullPointerException();
	}
	Class[] types = method.getParameterTypes();
	Object[] args = new Object[types.length];
	for (int i = 0; i < types.length; i++) {
	    args[i] = Util.unmarshalValue(types[i], in);
	}
	return args;
    }

    /**
     * Invokes the specified <code>method</code> on the specified remote
     * object <code>impl</code>, with the specified arguments.
     * If the invocation completes normally, the return value will be
     * returned by this method.  If the invocation throws an exception,
     * this method will throw the same exception.
     *
     * <p><code>BasicInvocationDispatcher</code> implements this method as
     * follows: 
     *
     * <p>If the specified method is not set accessible or is not a
     * <code>public</code> method of a <code>public</code> class an
     * <code>IllegalArgumentException</code> is thrown.
     *
     * <p>If the specified method is {@link ProxyTrust#getProxyVerifier
     * ProxyTrust.getProxyVerifier} and the remote object is an instance of
     * {@link ServerProxyTrust}, the {@link ServerProxyTrust#getProxyVerifier
     * getProxyVerifier} method of the remote object is called and the result
     * is returned.
     * 
     * <p>Otherwise, the specified method's <code>invoke</code> method is
     * called with the specified remote object and the specified arguments,
     * and the result is returned. If <code>invoke</code> throws an {@link
     * InvocationTargetException}, that exception is caught and the target
     * exception inside it is thrown to the caller. Any other exception
     * thrown during any of this computation is thrown to the caller.
     *
     * <p>A subclass can override this method to invoke the method in an
     * alternative context, perform pre- or post-processing, or otherwise
     * control how the method is invoked.
     *
     * @param	impl the remote object
     * @param	method the <code>Method</code> instance corresponding
     *		to the interface method invoked on the remote object
     * @param	args the method arguments
     * @param	context the server context passed to the {@link #dispatch
     *		dispatch} method for the remote call being processed
     * @return	the result of the method invocation on <code>impl</code>
     * @throws	NullPointerException if any argument is <code>null</code>
     * @throws	Throwable the exception thrown from the method invocation
     *		on <code>impl</code>
     **/
    protected Object invoke(Remote impl,
			    Method method,
			    Object[] args,
			    Collection context)
	throws Throwable
    {
	if (impl == null || args == null || context == null) {
	    throw new NullPointerException();
	}

	if (!method.isAccessible() &&
	    !(Modifier.isPublic(method.getDeclaringClass().getModifiers()) &&
	      Modifier.isPublic(method.getModifiers())))
	{
	    throw new IllegalArgumentException(
		"method not public or set accessible");
	}
	
	Class decl = method.getDeclaringClass();
	if (decl == ProxyTrust.class &&
	    method.getName().equals("getProxyVerifier") &&
	    impl instanceof ServerProxyTrust)
	{
	    if (args.length != 0) {
		throw new IllegalArgumentException("incorrect arguments");
	    }
	    return ((ServerProxyTrust) impl).getProxyVerifier();
	}
	
	try {
	    return method.invoke(impl, args);
	} catch (InvocationTargetException e) {
	    throw e.getTargetException();
	}
    }

    /**
     * Marshals the specified return value for the specified remote method
     * to the marshal output stream, <code>out</code>.  After invoking
     * the method on the remote object <code>impl</code>, the
     * <code>dispatch</code> method calls this method to marshal the value
     * returned from the invocation on that remote object.
     *
     * <p><code>BasicInvocationDispatcher</code> implements this method as
     * follows: 
     *
     * <p>If the declared return type of the method is void, then no return
     * value is written to the stream.  If the return type is a primitive
     * type, then the primitive value is written to the stream (for
     * example, if the type is <code>int.class</code>, then the primitive
     * <code>int</code> value is written to the stream using the
     * <code>writeInt</code> method).  Otherwise, the return value is
     * written to the stream using the <code>writeObject</code> method.
     *
     * <p>A subclass can override this method to marshal the return value in an
     * alternative context, perform pre- or post-processing on the return
     * value, marshal additional implicit data, or otherwise control how the
     * return value is marshalled. In general, the context used should mirror
     * the context in which the result is computed in the implementation of
     * the remote object.
     *
     * @param	impl the remote object
     * @param   method the <code>Method</code> instance corresponding
     *          to the interface method invoked on the remote object
     * @param   returnValue the return value to marshal to the stream
     * @param	out the marshal output stream
     * @param	context the server context passed to the {@link #dispatch
     *		dispatch} method for the remote call being processed
     * @throws	IOException if an I/O exception occurs
     * @throws	NullPointerException if <code>impl</code>,
     *		<code>method</code>, <code>out</code>, or
     *		<code>context</code> is <code>null</code>
     **/
    protected void marshalReturn(Remote impl,
				 Method method,
				 Object returnValue,
				 ObjectOutputStream out,
				 Collection context)
	throws IOException
    {
	if (impl == null || out == null || context == null) {
	    throw new NullPointerException();
	}
	Class returnType = method.getReturnType();
	if (returnType != void.class) {
	    Util.marshalValue(returnType, returnValue, out);
	}
    }

    /**
     * Marshals the <code>throwable</code> for the specified remote method
     * to the marshal output stream, <code>out</code>.  For each method
     * invocation on <code>impl</code> that throws an exception, this
     * method is called to marshal the throwable.  This method is also
     * called if an exception occurs reading the method information from
     * the incoming call stream, as a result of calling {@link
     * #unmarshalMethod unmarshalMethod}; in this case, the
     * <code>Method</code> instance will be <code>null</code>.
     *
     * <p><code>BasicInvocationDispatcher</code> implements this method to
     * marshal the throwable to the stream using the
     * <code>writeObject</code> method.
     *
     * <p>A subclass can override this method to marshal the throwable in an
     * alternative context, perform pre- or post-processing on the throwable,
     * marshal additional implicit data, or otherwise control how the throwable
     * is marshalled. In general, the context used should mirror the context
     * in which the exception is generated in the implementation of the
     * remote object.
     *
     * @param	impl the remote object
     * @param   method the possibly-<code>null</code> <code>Method</code>
     *		instance corresponding to the interface method invoked on
     *		the remote object
     * @param   throwable a throwable to marshal to the stream
     * @param	out the marshal output stream
     * @param 	context the server context
     * @throws	IOException if an I/O exception occurs
     * @throws	NullPointerException if <code>impl</code>,
     *		<code>throwable</code>, <code>out</code>, or
     *		<code>context</code> is <code>null</code>
     **/
    protected void marshalThrow(Remote impl,
				Method method,
				Throwable throwable,
				ObjectOutputStream out,
				Collection context)
	throws IOException
    {
	if (impl == null || throwable == null || context == null) {
	    throw new NullPointerException();
	}
	out.writeObject(throwable);
    }

    /**
     * Log the start of a remote call.
     */
    private void logCall(Remote impl, Method method, Object[] args) {
	String msg = "inbound call {0}.{1} to {2} from {3}\nclient {4}";
	if (logger.isLoggable(Level.FINEST)) {
	    msg = "inbound call {0}.{1} to {2} from {3}\nargs {5}\nclient {4}";
	}
	Subject client = getClientSubject();
	Set prins = (client != null) ? client.getPrincipals() : null;
	String host = null;
	try {
	    host = Util.getClientHostString();
	} catch (ServerNotActiveException e) {
	}
	logger.log(Level.FINE, msg,
		   new Object[]{method.getDeclaringClass().getName(),
				method.getName(), impl, host,
				prins, Arrays.asList(args)});
    }

    /**
     * Log the return of an inbound call.
     */
    private void logReturn(Remote impl, Method method, Object res) {
	String msg = "inbound call {0}.{1} to {2} returns";
	if (logger.isLoggable(Level.FINEST) &&
	    method.getReturnType() != void.class)
	{
	    msg = "inbound call {0}.{1} to {2} returns {3}";
	}
	logger.logp(Level.FINE, this.getClass().getName(), "dispatch", msg,
		    new Object[]{method.getDeclaringClass().getName(),
				 method.getName(), impl, res});
    }

    /**
     * Log the remote throw of an inbound call.
     */
    private void logThrow(Remote impl,
			  Method method,
			  Throwable t,
			  boolean fromImpl)
    {
	LogRecord lr = new LogRecord(
	  Levels.FAILED,
	  fromImpl ?
	  "inbound call {0}.{1} to {2} remotely throws" :
	  (logger.isLoggable(Level.FINEST) ?
	   "inbound call {0}.{1} to {2} dispatch remotely throws\nclient {3}" :
	   "inbound call {0}.{1} to {2} dispatch remotely throws"));
	lr.setLoggerName(logger.getName());
	lr.setSourceClassName(this.getClass().getName());
	lr.setSourceMethodName("dispatch");
	lr.setParameters(new Object[]{(method == null ?
				       "<unknown>" :
				       method.getDeclaringClass().getName()),
				      (method == null ?
				       "<unknown>" : method.getName()),
				      impl, getClientSubject()});
	lr.setThrown(t);
	logger.log(lr);
    }

    /**
     * Log the local throw of an inbound call.
     */
    private void logThrow(Remote impl, Throwable t) {
	LogRecord lr = new LogRecord(Levels.FAILED,
				     logger.isLoggable(Level.FINEST) ?
				     "{0} locally throws\nclient {1}" :
				     "{0} locally throws");
	lr.setLoggerName(logger.getName());
	lr.setSourceClassName(this.getClass().getName());
	lr.setSourceMethodName("dispatch");
	lr.setParameters(new Object[]{impl, getClientSubject()});
	lr.setThrown(t);
	logger.log(lr);
    }
    
    /**
     * Return the current client subject or <code>null</code> if not
     * currently executing a remote call.
     */
    private static Subject getClientSubject() {
	return (Subject) AccessController.doPrivileged(new PrivilegedAction() {
	    public Object run() {
		try {
		    return Util.getClientSubject();
		} catch (ServerNotActiveException e) {
		    return null;
		}
	    }
	});
    }
}
