Package net.jini.jeri

Source Code of net.jini.jeri.BasicInvocationDispatcher

/*
* 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;
    }
      }
  });
    }
}
TOP

Related Classes of net.jini.jeri.BasicInvocationDispatcher

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.