Package net.jini.jeri.ssl

Source Code of net.jini.jeri.ssl.SslEndpointImpl$ComparableConnectionContext

/*
* 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.ssl;

import com.sun.jini.jeri.internal.connection.BasicConnManagerFactory;
import com.sun.jini.jeri.internal.connection.ConnManager;
import com.sun.jini.jeri.internal.connection.ConnManagerFactory;
import com.sun.jini.jeri.internal.runtime.Util;
import com.sun.jini.logging.Levels;
import com.sun.jini.logging.LogUtil;
import java.io.IOException;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.Principal;
import java.security.PrivilegedAction;
import java.security.cert.CertPath;
import java.security.cert.X509Certificate;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.SocketFactory;
import javax.security.auth.Subject;
import javax.security.auth.x500.X500Principal;
import javax.security.auth.x500.X500PrivateCredential;
import net.jini.core.constraint.InvocationConstraints;
import net.jini.io.UnsupportedConstraintException;
import net.jini.jeri.Endpoint;
import net.jini.jeri.OutboundRequest;
import net.jini.jeri.OutboundRequestIterator;
import net.jini.jeri.connection.Connection;
import net.jini.jeri.connection.ConnectionEndpoint;
import net.jini.jeri.connection.OutboundRequestHandle;
import net.jini.security.AuthenticationPermission;

/**
* Provides the implementation of SslEndpoint so that the implementation can be
* inherited by HttpsEndpoint without revealing the inheritance in the public
* API.
*
* @author Sun Microsystems, Inc.
*/
class SslEndpointImpl extends Utilities implements ConnectionEndpoint {

    /* -- Fields -- */

    /** Client logger */
    static final Logger logger = clientLogger;

    /**
     * Weak key map that maps connection endpoints to weak references to the
     * associated ConnManager.  The weak values insure that the keys will not
     * be strongly held, since the connection manager has a strong reference to
     * the associated connection endpoint.
     */
    private static final Map connectionMgrs = new WeakHashMap();

    /** The size of the connection context cache. */
    private static final int CACHE_SIZE = 4;
   
    /** The factory for default connection managers. */
    private static final ConnManagerFactory connectionManagerFactory =
  new BasicConnManagerFactory();

    /** The associated endpoint. */
    final Endpoint endpoint;

    /** The name of the server host. */
    final String serverHost;

    /** The server port. */
    final int port;

    /** The factory for creating sockets, or null to use default sockets. */
    final SocketFactory socketFactory;

    /**
     * Whether to disable calling Socket.connect -- set when used by discovery
     * providers.
     */
    boolean disableSocketConnect;

    /** A cache for recently computed connection contexts. */
    private ConnectionContextCache[] connectionContextCache =
  new ConnectionContextCache[CACHE_SIZE];

    /** Next index for a connectionContextCache miss; counts down, not up. */
    private int cacheNext;

    /** The connection manager for this endpoint or null if not yet set. */
    ConnManager connectionManager;

    /* -- Constructors -- */

    /** Creates an instance of this class. */
    SslEndpointImpl(Endpoint endpoint,
        String serverHost,
        int port,
        SocketFactory socketFactory)
    {
  this.endpoint = endpoint;
  if (serverHost == null) {
      throw new NullPointerException("serverHost is null");
  }
  this.serverHost = serverHost;
  if (port <= 0 || port > 0xFFFF) {
      throw new IllegalArgumentException("Invalid port: " + port);
  }
  this.port = port;
  this.socketFactory = socketFactory;
    }

    /* -- Methods -- */

    /** Returns a string representation of this object. */
    public String toString() {
  return getClassName(this) + fieldsToString();
    }

    /** Returns a string representation of the fields of this object. */
    final String fieldsToString() {
  return "[" + serverHost + ":" + port +
      (socketFactory != null ? ", " + socketFactory : "") + "]";
    }

    /* -- Implement Endpoint -- */

    /** Returns a hash code value for this object. */
    public int hashCode() {
  return getClass().hashCode()
      ^ serverHost.hashCode()
      ^ port
      ^ (socketFactory != null ? socketFactory.hashCode() : 0);
    }

    /**
     * Two instances of this class are equal if they have the same actual
     * class; have the same values for server host and port; and have socket
     * factories that are either both null, or have the same actual class and
     * are equal.
     */
    public boolean equals(Object object) {
  if (object == null || object.getClass() != getClass()) {
      return false;
  }
  SslEndpointImpl other = (SslEndpointImpl) object;
  return serverHost.equals(other.serverHost)
      && port == other.port
      && Util.sameClassAndEquals(socketFactory, other.socketFactory);
    }

    /** Implements Endpoint.newRequest */
    final OutboundRequestIterator newRequest(
  InvocationConstraints constraints)
    {
  if (constraints == null) {
      throw new NullPointerException("Constraints cannot be null");
  }
  try {
      return newRequest(getCallContext(constraints));
  } catch (UnsupportedConstraintException e) {
      return new ExceptionOutboundRequestIterator(e);
  } catch (SecurityException e) {
      return new ExceptionOutboundRequestIterator(e);
  }
    }

    /**
     * An outbound request iterator that throws an IOException or a
     * SecurityException.
     */
    private static final class ExceptionOutboundRequestIterator
  implements OutboundRequestIterator
    {
  private final Exception exception;
  private boolean done = false;

  ExceptionOutboundRequestIterator(Exception exception) {
      this.exception = exception;
  }

  public synchronized boolean hasNext() {
      return !done;
  }

  public synchronized OutboundRequest next() throws IOException {
      if (done) {
    throw new NoSuchElementException();
      }
      done = true;
      if (exception instanceof SecurityException) {
    throw (SecurityException) exception;
      } else {
    throw (IOException) exception;
      }
  }
    }

    /** Implements Endpoint.newRequest when the constraints are supported. */
    OutboundRequestIterator newRequest(CallContext callContext) {
  return getConnectionManager().newRequest(callContext);
    }

    /** Returns the connection manager for this endpoint. */
    private ConnManager getConnectionManager() {
  synchronized (connectionMgrs) {
      if (connectionManager == null) {
    Reference ref = (Reference) connectionMgrs.get(this);
    connectionManager =
        (ref != null) ? (ConnManager) ref.get() : null;
    if (connectionManager == null) {
        connectionManager = connectionManagerFactory.create(this);
        connectionMgrs.put(
      this, new WeakReference(connectionManager));
    }
      }
      return connectionManager;
  }
    }

    /**
     * Returns a context for making a remote call with the specified
     * constraints and the current subject. This method does not perform
     * communication with the remote server.
     *
     * Throws a SecurityException if lack of authentication permissions
     * needed for requirements prevent the use of all contexts.
     *
     * Throws UnsupportedConstraintException if some requirements cannot
     * be satisfied.
     *
     * Returns a CallContext if throwing an exception or returning null
     * would reveal information about the current subject that the
     * caller does not have permission to know.
     */
    private CallContext getCallContext(InvocationConstraints constraints)
  throws UnsupportedConstraintException
    {
  final AccessControlContext acc = AccessController.getContext();
  Subject clientSubject = (Subject) AccessController.doPrivileged(
      new PrivilegedAction() {
    public Object run() {
        return Subject.getSubject(acc);
    }
      });
  Set clientPrincipals = getClientPrincipals(constraints.requirements());
  boolean requiredClient = clientPrincipals != null;
  boolean constrainedServer = getServerPrincipals(constraints) != null;
  Boolean getSubject = null;
  if (!requiredClient) {
      /* Try using principals from Subject instead */
      if (clientSubject == null) {
    clientPrincipals = Collections.EMPTY_SET;
      } else {
    /*
     * XXX: Work around BugID 4892841, Subject.getPrincipals(Class)
     * not thread-safe against changes to principals.
     * -tjb[18.Jul.2003]
     */
    synchronized (clientSubject.getPrincipals()) {
        clientPrincipals =
      clientSubject.getPrincipals(X500Principal.class);
    }
      }
      if (clientPrincipals.isEmpty()) {
    getSubject = getSubjectPermitted();
    if (getSubject == Boolean.FALSE) {
        /* Don't reveal that the client Subject has no principals.
         * Provide a dummy Principal (no credentials) which cannot
         * be authenticated in order to follow the same code path
         * as if there were a Subject principal.
         */
        clientPrincipals =
      Collections.singleton(UNKNOWN_PRINCIPAL);
    }
      }
  }

  /* Compute a list of ConnectionContexts between principals (as above):
   * (a) principals named in client constraints, if any; otherwise
   * (b) principals named in the client Subject, if any; otherwise
   * (c) a dummy principal or no principal
   * and the principals named in server constraints (if any). These
   * contexts will satisfy most constraints.
   */
  List contexts = new CopyOnRemoveList(
      getConnectionContexts(constraints, clientPrincipals));
  if (constrainedServer) {
      /* Server principals were named in constraints.  Remove from the
       * context list any ConnectionContexts for which there is no
       * permission to authenticate the context's client principal with
       * the context's server principal.
       */
      try {
    contexts = checkAuthenticationPermissions(contexts);
      } catch (SecurityException e) {
    /* This SecurityException indicates that there are no
     * remaining valid ConnectionContexts, either because there
     * are no client constraint principals and no client subject
     * principals, or because none of these principals was given
     * permission to authenticate with any server constraint
     * principal.
     *
     * If there were no client constraint principals, then
     * throwing an exception here would reveal that at least one
     * client subject principal existed and did not have
     * authentication permission.
     *
     * If there were client constraint principals, or if the
     * caller could determine Subject principals for itself, then
     * pass on the SecurityException.
     */
    if (!requiredClient && getSubject == null) {
        getSubject = getSubjectPermitted();
    }
    if (requiredClient || getSubject == Boolean.TRUE) {
        if (logger.isLoggable(Levels.FAILED)) {
      logThrow(
          logger, Levels.FAILED,
          SslEndpointImpl.class, "getCallContext",
          "new request for {0}\nwith {1}\nand {2}\nthrows",
          new Object[] { endpoint, constraints,
             subjectString(clientSubject) },
          e);
        }
        throw e;
    } else {
        /* Don't reveal that the client Subject has no principals.
         * Provide a dummy Principal (no credentials) which cannot
         * be authenticated in order to follow the same code path
         * as if there were a Subject principal.
         */
        CallContext result = createCallContext(
      getConnectionContexts(
          constraints,
          Collections.singleton(UNKNOWN_PRINCIPAL)),
      null);
        if (logger.isLoggable(Levels.FAILED)) {
      logThrow(
          logger, Levels.FAILED,
          SslEndpointImpl.class, "getCallContext",
          "new request for {0}\nwith {1}\nand {2}\n" +
          "will fail but cannot throw " +
          "because caller has no subject access\n" +
          "returns {3}\n" +
          "caught exception",
          new Object[] { endpoint, constraints,
             subjectString(clientSubject),
             result },
          e);
        }
        return result;
    }
      }
  }
  UnsupportedConstraintException unsupported = null;
  if (contexts.isEmpty()) {
      unsupported = new UnsupportedConstraintException(
    "Constraints not supported: " + constraints);
  } else {
      boolean checkSubject;
      if (constrainedServer) {
    checkSubject = true;
      } else {
    if (getSubject == null) {
        getSubject = getSubjectPermitted();
    }
    checkSubject = (getSubject == Boolean.TRUE);
      }
      if (checkSubject) {
    /* Check subject if caller has any access */
    try {
        contexts = checkSubject(contexts, clientSubject,
              constrainedServer, constraints);
    } catch (UnsupportedConstraintException e) {
        unsupported = e;
    }
      }
  }
  if (unsupported != null) {
      if (logger.isLoggable(Levels.FAILED)) {
    logThrow(logger, Levels.FAILED,
       SslEndpointImpl.class, "getCallContext",
       "new request for {0}\nwith {1}\nand {2}\nthrows",
       new Object[] { endpoint, constraints,
          subjectString(clientSubject) },
       unsupported);
      }
      throw unsupported;
  } else {
      CallContext result = createCallContext(contexts, clientSubject);
      if (logger.isLoggable(Level.FINE)) {
    logger.log(Level.FINE,
         "new request for {0}\nwith {1}\nand {2}\n" +
         "returns {3}",
         new Object[] {
             endpoint, constraints,
             subjectString(clientSubject), result
         });
      }
      return result;
  }
    }

    /** Convert connection contexts to a call context */
    private CallContext createCallContext(List contexts,
            Subject clientSubject)
    {
  boolean clientAuthRequired = true;
  boolean clientAuthPermitted = false;
  Set clientPrincipals = null;
  Set serverPrincipals = null;
  List suites = new ArrayList();
  boolean integrityRequired = false;
  boolean integrityPreferred = false;
  long connectionTimeout = -1;
  int max = contexts.size();
  for (int i = 0; i < max; i++) {
      ConnectionContext context = (ConnectionContext) contexts.get(i);
      if (context.client == null) {
    clientAuthRequired = false;
      } else {
    clientAuthPermitted = true;
    if (clientPrincipals == null) {
        clientPrincipals = new HashSet();
    }
    /*
     * Don't include the unknown principal, but still should record
     * that principals were specified by making clientPrincipals
     * non-null.
     */
    if (context.client != UNKNOWN_PRINCIPAL) {
        clientPrincipals.add(context.client);
    }
      }
      if (context.server != null &&
    context.server != UNKNOWN_PRINCIPAL)
      {
    if (serverPrincipals == null) {
        serverPrincipals = new HashSet();
    }
    serverPrincipals.add(context.server);
      }
      if (!suites.contains(context.cipherSuite)) {
    suites.add(context.cipherSuite);
      }
      if (context.getIntegrityRequired()) {
    integrityRequired = true;
      } else if (context.getIntegrityPreferred()) {
    integrityPreferred = true;
      }
      if (context.getConnectionTime() != -1 &&
    (connectionTimeout == -1 ||
     connectionTimeout > context.getConnectionTime()))
      {
    connectionTimeout = context.getConnectionTime();
      }
  }
  return new CallContext(
      endpoint, this,
      clientAuthPermitted ? clientSubject : null,
      clientAuthRequired, clientPrincipals, serverPrincipals, suites,
      integrityRequired, integrityPreferred, connectionTimeout);
    }

    /**
     * Returns a list of the contexts which are supported by principals and
     * credentials in the Subject.  Throws an UnsupportedConstraintException if
     * none of the contexts are supported, otherwise returns a non-empty list.
     */

    private static List checkSubject(List contexts,
             Subject clientSubject,
             boolean constrainedServer,
             InvocationConstraints constraints)
  throws UnsupportedConstraintException
    {
  Map publicCreds = getPublicCredentials(clientSubject);
  X500PrivateCredential[] privateCreds =
      (clientSubject != null && constrainedServer)
      ? (X500PrivateCredential[]) AccessController.doPrivileged(
    new SubjectCredentials.GetAllPrivateCredentialsAction(
        clientSubject))
      : new X500PrivateCredential[0];
  Set missingPublic = new HashSet();
  Set missingPrivate = new HashSet();
  /* Check principals and credentials */
      top:
  for (int i = contexts.size(); --i >= 0; ) {
      ConnectionContext context = (ConnectionContext) contexts.get(i);
      if (context.client == null) {
    continue;      /* Anonymous client OK */
      }
      Collection certs = (Collection) publicCreds.get(context.client);
      if (certs == null) {
    logger.log(Levels.HANDLED,
         "missing principal or public credentials: {0}",
         context.client);
    contexts.remove(i);
    missingPublic.add(context.client);
      } else if (constrainedServer) {
    /*
     * Checked authentication permissions, so OK to check private
     * credentials.
     */
    for (int j = privateCreds.length; --j >= 0; ) {
        X509Certificate cert = privateCreds[j].getCertificate();
        if (cert != null) {    /* null if destroyed */
      if (certs.contains(cert)) {
          continue top;  /* Private credentials OK */
      }
        }
    }
    logger.log(Levels.HANDLED, "missing private credentials: {0}",
         context.client);
    contexts.remove(i);
    missingPrivate.add(context.client);
      }
  }
  if (!contexts.isEmpty()) {
      return contexts;
  } else {
      throw new UnsupportedConstraintException(
    "Constraints not supported: " + constraints + ";" +
    (missingPublic.isEmpty() ? ""
     : ("\nmissing principals or public credentials: " +
        missingPublic)) +
    (missingPrivate.isEmpty() ? ""
     : "\nmissing private credentials: " + missingPrivate));
  }
    }

    /**
     * Checks if the caller has permission to get the current subject,
     * returning Boolean.TRUE or FALSE.
     */
    private static Boolean getSubjectPermitted() {
  SecurityManager sm = System.getSecurityManager();
  if (sm != null) {
      try {
    sm.checkPermission(getSubjectPermission);
      } catch (SecurityException e) {
    return Boolean.FALSE;
      }
  }
  return Boolean.TRUE;
    }

    /**
     * Removes the contexts for which the client does not have authentication
     * permission.  Throws SecurityException if lack of permission prevents any
     * contexts from being used.
     */
    private static List checkAuthenticationPermissions(List contexts) {
  if (contexts.isEmpty()) {
      return contexts;
  }
  SecurityManager sm = System.getSecurityManager();
  if (sm == null) {
      return contexts;
  }
  Map perms = new HashMap();
  Set exceptions = new HashSet();
  for (int i = contexts.size(); -- i >= 0; ) {
      ConnectionContext context = (ConnectionContext) contexts.get(i);
      if (context.client == null) {
    /* Client anonymous -- OK */
    continue;
      } else if (context.server == UNKNOWN_PRINCIPAL) {
    /* Server not known -- can't check any permissions */
    break;
      }
      AuthenticationPermission p = new AuthenticationPermission(
    Collections.singleton(context.client),
    Collections.singleton(context.server),
    "connect");
      Object value = perms.get(p);
      if (Boolean.FALSE.equals(value)) {
    contexts.remove(i);
      } else if (!Boolean.TRUE.equals(value)) {
    try {
        sm.checkPermission(p);
        perms.put(p, Boolean.TRUE);
    } catch (SecurityException e) {
        logger.log(
      Levels.HANDLED,
      "check authentication permission caught exception",
      e);
        perms.put(p, Boolean.FALSE);
        exceptions.add(e);
        contexts.remove(i);
    }
      }
  }
  if (!contexts.isEmpty()) {
      return contexts;
  } else if (exceptions.size() == 1) {
      throw (SecurityException) exceptions.iterator().next();
  } else {
      throw new SecurityException(exceptions.toString());
  }
    }

    /**
     * Returns a map that maps each principal in the subject to a set of the
     * associated X.500 public credentials.
     */
    private static Map getPublicCredentials(Subject subject) {
  Map publicCreds = new HashMap();
  List certPaths = SubjectCredentials.getCertificateChains(subject);
  if (certPaths != null) {
      for (int i = certPaths.size(); --i >= 0; ) {
    CertPath chain = (CertPath) certPaths.get(i);
    X509Certificate cert = SubjectCredentials.firstX509Cert(chain);
    X500Principal p =
        SubjectCredentials.getPrincipal(subject, cert);
    if (p != null) {
        Collection certs = (Collection) publicCreds.get(p);
        if (certs == null) {
      certs = new ArrayList(1);
      publicCreds.put(p, certs);
        }
        certs.add(cert);
    }
      }
  }
  return publicCreds;
    }

    /**
     * A List that supports removing items by making a copy of the underlying
     * list.
     */
    private static final class CopyOnRemoveList extends AbstractList {
  private List list;
  private boolean modified;

  CopyOnRemoveList(List list) {
      this.list = list;
  }

  public Object get(int index) {
      return list.get(index);
  }

  public int size() {
      return list.size();
  }

  public Object remove(int index) {
      if (!modified) {
    list = new ArrayList(list);
    modified = true;
      }
      return list.remove(index);
  }
    }

    /**
     * Defines a structure to cache a ConnectionContexts for specific
     * constraints and client principals.
     */
    private static final class ConnectionContextCache {
  final InvocationConstraints constraints;
  final Set clientPrincipals;
  final List connectionContexts;

  ConnectionContextCache(InvocationConstraints constraints,
             Set clientPrincipals,
             List connectionContexts)
  {
      this.constraints = constraints;
      this.clientPrincipals = clientPrincipals;
      this.connectionContexts = connectionContexts;
  }
    }

    /**
     * Gets an unmodifiable list of the ConnectionContexts for the
     * specified constraints and client principals.
     */
    private List getConnectionContexts(InvocationConstraints constraints,
               Set clientPrincipals)
    {
  synchronized (connectionContextCache) {
      for (int i = CACHE_SIZE; --i >= 0; ) {
    ConnectionContextCache cache = connectionContextCache[i];
    if (cache != null &&
        cache.constraints.equals(constraints) &&
        cache.clientPrincipals.equals(clientPrincipals))
    {
        logger.log(Level.FINEST, "used connection cache");
        return cache.connectionContexts;
    }
      }
  }
  Set serverPrincipals = getServerPrincipals(constraints);
  if (serverPrincipals == null) {
      serverPrincipals = Collections.singleton(UNKNOWN_PRINCIPAL);
  }
  List contexts = Collections.unmodifiableList(
      computeConnectionContexts(
    getSupportedCipherSuites(), clientPrincipals, serverPrincipals,
    constraints));
  synchronized (connectionContextCache) {
      connectionContextCache[cacheNext] =
    new ConnectionContextCache(
        constraints, clientPrincipals, contexts);
      if (cacheNext == 0) {
    cacheNext = CACHE_SIZE;
      }
      cacheNext--;
  }
  return contexts;
    }

    /** Used for sorting ConnectionContexts by preferences and suite order. */
    private static final class ComparableConnectionContext
  implements Comparable
    {
  final ConnectionContext context;
  private final int suiteIndex;

  ComparableConnectionContext(ConnectionContext context,
            int suiteIndex)
  {
      this.context = context;
      this.suiteIndex = suiteIndex;
  }

  public int compareTo(Object object) {
      ComparableConnectionContext other =
    (ComparableConnectionContext) object;
      /* Lower value for more preferences */
      int result =
    other.context.getPreferences() - context.getPreferences();
      if (result == 0) {
    /* Lower value for lower index */
    result = suiteIndex - other.suiteIndex;
      }
      return result;
  }

  public String toString() {
      StringBuffer sb = new StringBuffer("ComparableConnectionContext[");
      context.fieldsToString(sb);
      sb.append(", index: ").append(suiteIndex);
      sb.append("]");
      return sb.toString();
  }
    }

    /**
     * Computes a list of ConnectionContexts for the specified set of
     * suites, client and server principals, and constraints, sorted by
     * preferences and suite order.
     */
    private static List computeConnectionContexts(
  String[] suites,
  Set clients,
  Set servers,
  InvocationConstraints constraints)
    {
  List result = new ArrayList(
      suites.length * (clients.size() + 1) * (servers.size() + 1));
  for (int suiteIndex = suites.length; --suiteIndex >= 0; ) {
      String suite = suites[suiteIndex];
      Iterator cIter = clients.iterator();
      Principal client;
      do {
    if (cIter.hasNext()) {
        client = (Principal) cIter.next();
        assert client != null;
    } else {
        client = null;
    }
    Iterator sIter = servers.iterator();
    Principal server;
    do {
        if (sIter.hasNext()) {
      server = (Principal) sIter.next();
      assert server != null;
        } else {
      server = null;
        }
        for (int i = 2; --i >= 0; ) {
      boolean integrity = i == 0;
      ConnectionContext context =
          ConnectionContext.getInstance(
        suite, client, server, integrity,
        true /* clientSide */, constraints);
      if (context != null) {
          result.add(
        new ComparableConnectionContext(
            context, suiteIndex));
      }
        }
    } while (server != null);
      } while (client != null);
  }
  Collections.sort(result);
  logger.log(Level.FINEST, "compute connection contexts produces {0}",
       result);
  for (int i = result.size(); --i >= 0; ) {
      ComparableConnectionContext ccc =
    (ComparableConnectionContext) result.get(i);
      result.set(i, ccc.context);
  }
  return result;
    }

    /* -- Implement ConnectionEndpoint -- */

    /** Creates a new connection. */
    public Connection connect(OutboundRequestHandle handle) throws IOException {
  SslConnection connection = new SslConnection(
      CallContext.coerce(handle, endpoint), serverHost, port,
      socketFactory);
  connection.establishCallContext();
  return connection;
    }

    /** Chooses a connection from existing connections. */
    public Connection connect(OutboundRequestHandle handle,
            Collection active,
            Collection idle)
    {
  CallContext context = CallContext.coerce(handle, endpoint);
  if (active == null || idle == null) {
      throw new NullPointerException("Arguments cannot be null");
  }
  Connection result = null;
  /*
   * Choose the first connection with an appropriate subject, an active
   * suite that is one of the requested suites, and one for which all the
   * requested suites better than the active one were also better for its
   * call context.  That insures that we've gotten the best connection we
   * can, assuming a new handshake were to make similar decisions.
   */
  boolean checkedResolvePermission = false;
  for (Iterator iter = new ConnectionsIterator(endpoint, active, idle);
       iter.hasNext(); )
  {
      SslConnection connection = (SslConnection) iter.next();
      if (connection.useFor(context)) {
    String phost = connection.getProxyHost();
    boolean usingProxy = (phost.length() != 0);

    if (usingProxy) {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
      try {
          sm.checkConnect(serverHost, port);
      } catch (SecurityException e) {
          if (logger.isLoggable(Levels.FAILED)) {
        logThrow(logger, Levels.FAILED,
           SslEndpointImpl.class, "connect",
           "choose connection for {0}\nthrows",
           new Object[] { this }, e);
          }
          throw e;
      }
        }
    } else {
        if (!checkedResolvePermission) {
      try {
          checkResolvePermission();
      } catch (SecurityException e) {
          if (logger.isLoggable(Levels.FAILED)) {
        LogUtil.logThrow(logger, Levels.FAILED,
             SslEndpointImpl.class, "connect",
             "exception resolving host {0}",
             new Object[] { serverHost }, e);
          }
          throw e;
      }
      checkedResolvePermission = true;
        }
        try {
      if (!connection.checkConnectPermission()) {
          continue;
      }
        } catch (SecurityException e) {
      if (logger.isLoggable(Levels.HANDLED)) {
          LogUtil.logThrow(logger, Levels.HANDLED,
        SslEndpointImpl.class, "nextRequest",
        "access to reuse connection {0} denied",
        new Object[] { connection }, e);
      }
      continue;
        }
    }
    result = connection;
    break;
      }
  }
  if (logger.isLoggable(Level.FINE)) {
      logger.log(Level.FINE,
           "choose connection for {0}\nwith active {1}\n" +
           "and idle {2}\nreturns {3}",
           new Object[] { handle, active, idle, result });
  }
  return result;
    }

    private void checkResolvePermission() {
  SecurityManager sm = System.getSecurityManager();
  if (sm != null) {
      sm.checkConnect(serverHost, -1);
  }
    }

    /**
     * Defines an iterator over active and idle connections which performs
     * error checking on connections.
     */
    private static final class ConnectionsIterator implements Iterator {
  private final Endpoint endpoint;
  private Collection active;  /* Set to null when exhausted */
  private final Collection idle;
  private Iterator iter;

  ConnectionsIterator(Endpoint endpoint,
          Collection active,
          Collection idle)
  {
      this.endpoint = endpoint;
      this.active = active;
      this.idle = idle;
      iter = active.iterator();
      if (!iter.hasNext()) {
    this.active = null;
    iter = idle.iterator();
      }
  }

  public boolean hasNext() {
      return iter.hasNext();
  }

  public Object next() {
      if (!hasNext()) {
    throw new NoSuchElementException();
      }
      Object next = iter.next();
      if (next == null) {
    throw new NullPointerException("Connection cannot be null");
      } else if (!(next instanceof SslConnection)) {
    throw new IllegalArgumentException(
        "Connection must be of type SslConnection: " + next);
      }
      SslConnection result = (SslConnection) next;
      if (!endpoint.equals(result.callContext.endpoint)) {
    throw new IllegalArgumentException(
        "Connection has wrong endpoint: found " +
        result.callContext.endpoint + ", expected " + endpoint);
      }
      if (!iter.hasNext() && active != null) {
    active = null;
    iter = idle.iterator();
      }
      return result;
  }

  public void remove() {
      throw new UnsupportedOperationException();
  }
    }
}
TOP

Related Classes of net.jini.jeri.ssl.SslEndpointImpl$ComparableConnectionContext

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.