Package net.jini.jeri.tcp

Source Code of net.jini.jeri.tcp.TcpServerEndpoint$LE

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

import com.sun.jini.action.GetBooleanAction;
import com.sun.jini.jeri.internal.runtime.Util;
import com.sun.jini.logging.Levels;
import com.sun.jini.logging.LogUtil;
import com.sun.jini.thread.Executor;
import com.sun.jini.thread.GetThreadPoolAction;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.nio.channels.SocketChannel;
import java.nio.channels.ServerSocketChannel;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import net.jini.core.constraint.InvocationConstraints;
import net.jini.io.context.ClientHost;
import net.jini.io.UnsupportedConstraintException;
import net.jini.jeri.Endpoint;
import net.jini.jeri.InboundRequest;
import net.jini.jeri.RequestDispatcher;
import net.jini.jeri.ServerEndpoint;
import net.jini.jeri.connection.InboundRequestHandle;
import net.jini.jeri.connection.ServerConnection;
import net.jini.jeri.connection.ServerConnectionManager;
import net.jini.security.Security;
import net.jini.security.SecurityContext;

/**
* An implementation of the {@link ServerEndpoint} abstraction that
* uses TCP sockets (instances of {@link ServerSocket}) for the
* underlying communication mechanism.
*
* <p><code>TcpServerEndpoint</code> instances contain a host name and
* a TCP port number, as well as an optional {@link
* ServerSocketFactory} for customizing the type of
* <code>ServerSocket</code> to use and an optional {@link
* SocketFactory} for customizing the type of {@link Socket} that
* client endpoints will use.  The port number is the local TCP port
* to bind to when listening for incoming socket connections.  If the
* port number is zero, then each listen operation will bind to a free
* (non-zero) port, which will be the port number contained in the
* resulting {@link TcpEndpoint}.  The host name contained in a
* <code>TcpServerEndpoint</code> controls the host name that will be
* contained in the <code>TcpEndpoint</code> instances produced when
* {@link #enumerateListenEndpoints enumerateListenEndpoints} is
* invoked to listen on the <code>TcpServerEndpoint</code> (this host
* name does not affect the behavior of listen operations themselves).
* If the host name in a <code>TcpServerEndpoint</code> is
* <code>null</code>, then the host name in the
* <code>TcpEndpoint</code> instances that it produces will be the IP
* address string obtained from {@link InetAddress#getLocalHost
* InetAddress.getLocalHost} when {@link #enumerateListenEndpoints
* enumerateListenEndpoints} is invoked.
*
* <p><code>TcpServerEndpoint</code> uses the <a
* href="../connection/doc-files/mux.html">Jini extensible remote
* invocation (Jini ERI) multiplexing protocol</a> to map incoming
* requests to socket connections.
*
* <p>A <code>ServerSocketFactory</code> used with a
* <code>TcpServerEndpoint</code> must implement {@link Object#equals
* Object.equals} to obey the guidelines that are specified for
* <code>equals</code> methods of {@link
* net.jini.jeri.ServerEndpoint.ListenEndpoint ListenEndpoint}
* instances.  A <code>SocketFactory</code> used with a
* <code>TcpServerEndpoint</code> should be serializable and must
* implement <code>Object.equals</code> to obey the guidelines that
* are specified for <code>equals</code> methods of {@link Endpoint}
* instances.
*
* @author Sun Microsystems, Inc.
* @see TcpEndpoint
* @since 2.0
**/
public final class TcpServerEndpoint implements ServerEndpoint {

    /**
     * pool of threads for executing tasks in system thread group:
     * used for TCP accept threads
     **/
    private static final Executor systemThreadPool =
  (Executor) AccessController.doPrivileged(
      new GetThreadPoolAction(false));

    private static final ServerConnectionManager serverConnectionManager =
  new ServerConnectionManager();

    /** server transport logger */
    private static final Logger logger =
  Logger.getLogger("net.jini.jeri.tcp.server");

    /** whether or not to use NIO-based sockets if possible */
    private static final boolean useNIO =    // default false
  ((Boolean) AccessController.doPrivileged(new GetBooleanAction(
      "com.sun.jini.jeri.tcp.useNIO"))).booleanValue();

    /** name for local host to fill in to corresponding TcpEndpoints */
    private final String host;

    /** the TCP port that this TcpServerEndpoint listens on */
    private final int port;

    /**
     * the socket factory that TcpEndpoint objects produced by
     * listening on this TcpServerEndpoint will use to create Socket
     * objects
     **/
    private final SocketFactory sf;

    /**
     * the socket factory that this TcpServerEndpoint uses to create
     * ServerSocket objects
     **/
    private final ServerSocketFactory ssf;

    /**
     * Returns a <code>TcpServerEndpoint</code> instance for the given
     * TCP port number.
     *
     * <p>The host name contained in the returned
     * <code>TcpServerEndpoint</code> will be <code>null</code>, so
     * that when its {@link #enumerateListenEndpoints
     * enumerateListenEndpoints} method produces a {@link
     * TcpEndpoint}, the <code>TcpEndpoint</code>'s host name will be
     * the IP address string obtained from {@link
     * InetAddress#getLocalHost InetAddress.getLocalHost}.
     *
     * <p>The <code>ServerSocketFactory</code> contained in the
     * returned <code>TcpServerEndpoint</code> will be
     * <code>null</code>, indicating that this endpoint will create
     * <code>ServerSocket</code> objects directly.  The
     * <code>SocketFactory</code> contained in the returned
     * <code>TcpServerEndpoint</code> will also be <code>null</code>.
     *
     * @param port the TCP port on the local host to listen on
     *
     * @return a <code>TcpServerEndpoint</code> instance
     *
     * @throws IllegalArgumentException if the port number is out of
     * the range <code>0</code> to <code>65535</code> (inclusive)
     **/
    public static TcpServerEndpoint getInstance(int port) {
  return getInstance(null, port, null, null);
    }

    /**
     * Returns a <code>TcpServerEndpoint</code> instance for the given
     * host name and TCP port number.
     *
     * <p>If <code>host</code> is <code>null</code>, then when the
     * returned <code>TcpServerEndpoint</code>'s {@link
     * #enumerateListenEndpoints enumerateListenEndpoints} method
     * produces a {@link TcpEndpoint}, the <code>TcpEndpoint</code>'s
     * host name will be the IP address string obtained from {@link
     * InetAddress#getLocalHost InetAddress.getLocalHost}.
     *
     * <p>The <code>ServerSocketFactory</code> contained in the
     * returned <code>TcpServerEndpoint</code> will be
     * <code>null</code>, indicating that this endpoint will create
     * <code>ServerSocket</code> objects directly.  The
     * <code>SocketFactory</code> contained in the returned
     * <code>TcpServerEndpoint</code> will also be <code>null</code>.
     *
     * @param host the host name to be used in
     * <code>TcpEndpoint</code> instances produced by listening on the
     * returned <code>TcpServerEndpoint</code>, or <code>null</code>
     *
     * @param port the TCP port on the local host to listen on
     *
     * @return a <code>TcpServerEndpoint</code> instance
     *
     * @throws IllegalArgumentException if the port number is out of
     * the range <code>0</code> to <code>65535</code> (inclusive)
     **/
    public static TcpServerEndpoint getInstance(String host, int port) {
  return getInstance(host, port, null, null);
    }

    /**
     * Returns a <code>TcpServerEndpoint</code> instance for the given
     * host name and TCP port number that contains the given
     * <code>SocketFactory</code> and
     * <code>ServerSocketFactory</code>.
     *
     * <p>If <code>host</code> is <code>null</code>, then when the
     * returned <code>TcpServerEndpoint</code>'s {@link
     * #enumerateListenEndpoints enumerateListenEndpoints} method
     * produces a {@link TcpEndpoint}, the <code>TcpEndpoint</code>'s
     * host name will be the IP address string obtained from {@link
     * InetAddress#getLocalHost InetAddress.getLocalHost}.
     *
     * <p>If the server socket factory argument is <code>null</code>,
     * then this endpoint will create <code>ServerSocket</code>
     * objects directly.
     *
     * @param host the host name to be used in
     * <code>TcpEndpoint</code> instances produced by listening on the
     * returned <code>TcpServerEndpoint</code>, or <code>null</code>
     *
     * @param port the TCP port on the local host to listen on
     *
     * @param sf the <code>SocketFactory</code> to use for this
     * <code>TcpServerEndpoint</code>, or <code>null</code>
     *
     * @param ssf the <code>ServerSocketFactory</code> to use for this
     * <code>TcpServerEndpoint</code>, or <code>null</code>
     *
     * @return a <code>TcpServerEndpoint</code> instance
     *
     * @throws IllegalArgumentException if the port number is out of
     * the range <code>0</code> to <code>65535</code> (inclusive)
     **/
    public static TcpServerEndpoint getInstance(String host, int port,
            SocketFactory sf,
            ServerSocketFactory ssf)
    {
  return new TcpServerEndpoint(host, port, sf, ssf);
    }

    /**
     * Constructs a new instance.
     **/
    private TcpServerEndpoint(String host, int port,
            SocketFactory sf,
            ServerSocketFactory ssf)
    {
  if (port < 0 || port > 0xFFFF) {
      throw new IllegalArgumentException(
    "port number out of range: " + port);
  }
  this.host = host;
  this.port = port;
  this.sf = sf;
  this.ssf = ssf;
    }

    /**
     * Returns the host name that will be used in
     * <code>TcpEndpoint</code> instances produced by listening on
     * this <code>TcpServerEndpoint</code>, or <code>null</code> if
     * the IP address string obtained from {@link
     * InetAddress#getLocalHost InetAddress.getLocalHost} will be
     * used.
     *
     * @return the host name to use in <code>TcpEndpoint</code>
     * instances produced from this object, or <code>null</code>
     **/
    public String getHost() {
  return host;
    }

    /**
     * Returns the TCP port that this <code>TcpServerEndpoint</code>
     * listens on.
     *
     * @return the TCP port that this endpoint listens on
     **/
    public int getPort() {
  return port;
    }

    /**
     * Returns the <code>SocketFactory</code> that
     * <code>TcpEndpoint</code> objects produced by listening on this
     * <code>TcpServerEndpoint</code> will use to create
     * <code>Socket</code> objects.
     *
     * @return the socket factory that client-side endpoints
     * corresponding to this server endpoint will use to create
     * sockets, or <code>null</code> if no factory will be used
     **/
    public SocketFactory getSocketFactory() {
  return sf;
    }

    /**
     * Returns the <code>ServerSocketFactory</code> that this endpoint
     * uses to create <code>ServerSocket</code> objects.
     *
     * @return the socket factory that this endpoint uses to create
     * sockets, or <code>null</code> if no factory is used
     **/
    public ServerSocketFactory getServerSocketFactory() {
  return ssf;
    }

    /**
     * {@inheritDoc}
     *
     * @throws NullPointerException {@inheritDoc}
     **/
    public InvocationConstraints checkConstraints(
  InvocationConstraints constraints)
  throws UnsupportedConstraintException
    {
  return Constraints.check(constraints, true);
    }

    /**
     * Passes the {@link net.jini.jeri.ServerEndpoint.ListenEndpoint
     * ListenEndpoint} for this <code>TcpServerEndpoint</code> to
     * <code>listenContext</code>, which will ensure an active listen
     * operation on the endpoint, and returns a
     * <code>TcpEndpoint</code> instance corresponding to the listen
     * operation chosen by <code>listenContext</code>.
     *
     * <p>If this <code>TcpServerEndpoint</code>'s host name is not
     * <code>null</code>, then the returned <code>TcpEndpoint</code>
     * will contain that host name.  If this
     * <code>TcpServerEndpoint</code>'s host name is
     * <code>null</code>, then this method invokes {@link
     * InetAddress#getLocalHost InetAddress.getLocalHost} to obtain an
     * <code>InetAddress</code> for the local host.  If
     * <code>InetAddress.getLocalHost</code> throws an {@link
     * UnknownHostException}, this method throws an
     * <code>UnknownHostException</code>.  The returned
     * <code>TcpEndpoint</code>'s host name will be the string
     * returned by invoking {@link InetAddress#getHostAddress
     * getHostAddress} on that <code>InetAddress</code>.  If there is
     * a security manager, its {@link
     * SecurityManager#checkConnect(String,int) checkConnect} method
     * will be invoked with the string returned by invoking {@link
     * InetAddress#getHostName getHostName} on that same
     * <code>InetAddress</code> as the host argument and
     * <code>-1</code> as the port argument; this could result in a
     * <code>SecurityException</code>.
     *
     * <p>This method invokes <code>addListenEndpoint</code> on
     * <code>listenContext</code> once, passing a
     * <code>ListenEndpoint</code> as described below.  If
     * <code>addListenEndpoint</code> throws an exception, then this
     * method throws that exception.  Otherwise, this method returns a
     * <code>TcpEndpoint</code> instance with the host name described
     * above, the TCP port number bound by the listen operation
     * represented by the {@link
     * net.jini.jeri.ServerEndpoint.ListenHandle ListenHandle}
     * returned by <code>addListenEndpoint</code>, and the same
     * <code>SocketFactory</code> as this
     * <code>TcpServerEndpoint</code>.
     *
     * <p>The <code>ListenEndpoint</code> passed to
     * <code>addListenEndpoint</code> represents the TCP port number
     * and <code>ServerSocketFactory</code> of this
     * <code>TcpServerEndpoint</code>.  Its methods behave as follows:
     *
     * <p>{@link net.jini.jeri.ServerEndpoint.ListenEndpoint#listen
     * ListenHandle listen(RequestDispatcher)}:
     *
     * <blockquote>
     *
     * Listens for requests received on this endpoint's TCP port,
     * dispatching them to the supplied <code>RequestDispatcher</code>
     * in the form of {@link InboundRequest} instances.
     *
     * <p>When the implementation of this method needs to create a new
     * <code>ServerSocket</code>, it will do so by invoking one of the
     * <code>createServerSocket</code> methods that returns a bound
     * server socket on the contained <code>ServerSocketFactory</code>
     * if non-<code>null</code>, or it will create a
     * <code>ServerSocket</code> directly otherwise.
     *
     * <p>If there is a security manager, its {@link
     * SecurityManager#checkListen checkListen} method will be invoked
     * with this endpoint's TCP port; this could result in a
     * <code>SecurityException</code>.  Furthermore, before a given
     * <code>InboundRequest</code> gets dispatched to the supplied
     * request dispatcher, the security manager's {@link
     * SecurityManager#checkAccept checkAccept} method must have been
     * successfully invoked in the security context of this
     * <code>listen</code> invocation with the remote IP address and
     * port of the <code>Socket</code> used to receive the request.
     * The <code>checkPermissions</code> method of the dispatched
     * <code>InboundRequest</code> also performs this latter security
     * check.  (Note that in some cases, the implementation may carry
     * out these security checks indirectly, such as through
     * invocations of <code>ServerSocket</code>'s constructors or
     * <code>accept</code> method.)
     *
     * <p>Requests will be dispatched in a {@link PrivilegedAction}
     * wrapped by a {@link SecurityContext} obtained when this method
     * was invoked, with the {@link AccessControlContext} of that
     * <code>SecurityContext</code> in effect.
     *
     * <p>Dispatched requests will implement {@link
     * InboundRequest#populateContext populateContext} to populate the
     * given collection with an element that implements the {@link
     * ClientHost} interface.  That <code>ClientHost</code> element
     * implements {@link ClientHost#getClientHost getClientHost} to
     * return the IP address of the <code>Socket</code> that the
     * request was received over (see {@link Socket#getInetAddress}).
     *
     * <p>Throws {@link IOException} if an I/O exception occurs while
     * performing this operation, such as if the TCP port is already
     * in use.
     *
     * <p>Throws {@link SecurityException} if there is a security
     * manager and the invocation of its <code>checkListen</code>
     * method fails.
     *
     * <p>Throws {@link NullPointerException} if
     * <code>requestDispatcher</code> is <code>null</code>
     *
     * </blockquote>
     *
     * <p>{@link
     * net.jini.jeri.ServerEndpoint.ListenEndpoint#checkPermissions
     * void checkPermissions()}:
     *
     * <blockquote>
     *
     * Verifies that the current security context has all of the
     * security permissions necessary to listen for requests on this
     * endpoint.
     *
     * <p>If there is a security manager, its <code>checkListen</code>
     * method will be invoked with this endpoint's TCP port; this
     * could result in a <code>SecurityException</code>.
     *
     * <p>Throws {@link SecurityException} if there is a security
     * manager and the invocation of its <code>checkListen</code>
     * method fails.
     *
     * </blockquote>
     *
     * <p>{@link Object#equals boolean equals(Object)}:
     *
     * <blockquote>
     *
     * Compares the specified object with this
     * <code>ListenEndpoint</code> for equality.
     *
     * <p>This method returns <code>true</code> if and only if
     *
     * <ul>
     *
     * <li>the specified object is also a <code>ListenEndpoint</code>
     * produced by a <code>TcpServerEndpoint</code>,
     *
     * <li>the port in the specified object is equal to the port in
     * this object, and
     *
     * <li>either this object and the specified object both have no
     * <code>ServerSocketFactory</code> or the
     * <code>ServerSocketFactory</code> in the specified object has
     * the same class and is equal to the one in this object.
     *
     * </ul>
     *
     * </blockquote>
     *
     * @param listenContext the <code>ListenContext</code> to pass
     * this <code>TcpServerEndpoint</code>'s
     * <code>ListenEndpoint</code> to
     *
     * @return the <code>TcpEndpoint</code> instance for sending
     * requests to this <code>TcpServerEndpoint</code>'s endpoint
     * being listened on
     *
     * @throws UnknownHostException if this
     * <code>TcpServerEndpoint</code>'s host name is <code>null</code>
     * and <code>InetAddress.getLocalHost</code> throws an
     * <code>UnknownHostException</code>
     *
     * @throws IOException if an I/O exception occurs while performing
     * this operation, such as if the TCP port is already in use
     *
     * @throws SecurityException if there is a security manager and
     * either the invocation of its <code>checkListen</code> method
     * fails or this <code>TcpServerEndpoint</code>'s host name is
     * <code>null</code> and the invocation of the security manager's
     * <code>checkConnect</code> method fails
     *
     * @throws IllegalArgumentException {@inheritDoc}
     *
     * @throws NullPointerException {@inheritDoc}
     **/
    public Endpoint enumerateListenEndpoints(ListenContext listenContext)
  throws IOException
    {
  if (listenContext == null) {
      throw new NullPointerException();
  }

  String localHost = host;
  if (localHost == null) {
      InetAddress localAddr;
      try {
    localAddr = (InetAddress) AccessController.doPrivileged(
        new PrivilegedExceptionAction() {
      public Object run() throws UnknownHostException {
          return InetAddress.getLocalHost();
      }
        });
      } catch (PrivilegedActionException e) {
    /*
     * Only expose UnknownHostException thrown directly by
     * InetAddress.getLocalHost if it would also be thrown
     * in the caller's security context; otherwise, throw
     * a new UnknownHostException without the host name.
     */
    InetAddress.getLocalHost();
    throw new UnknownHostException(
        "access to resolve local host denied");
      }
      SecurityManager sm = System.getSecurityManager();
      if (sm != null) {
    try {
        sm.checkConnect(localAddr.getHostName(), -1);
    } catch (SecurityException e) {
        throw new SecurityException(
      "access to resolve local host denied");
    }
      }
      localHost = localAddr.getHostAddress();
  }

  LE listenEndpoint = new LE(); // REMIND: needn't be new?
  ListenCookie listenCookie =
      listenContext.addListenEndpoint(listenEndpoint);

  if (!(listenCookie instanceof LE.Cookie)) {
      throw new IllegalArgumentException();
  }
  LE.Cookie cookie = (LE.Cookie) listenCookie;
  if (!listenEndpoint.equals(cookie.getLE())) {
      throw new IllegalArgumentException();
  }

  return TcpEndpoint.getInstance(localHost, cookie.getPort(), sf);
    }

    /**
     * Returns the hash code value for this
     * <code>TcpServerEndpoint</code>.
     *
     * @return the hash code value for this
     * <code>TcpServerEndpoint</code>
     **/
    public int hashCode() {
  return port ^
      (host != null ? host.hashCode() : 0) ^
      (sf != null ? sf.hashCode() : 0) ^
      (ssf != null ? ssf.hashCode() : 0);
    }

    /**
     * Compares the specified object with this
     * <code>TcpServerEndpoint</code> for equality.
     *
     * <p>This method returns <code>true</code> if and only if
     *
     * <ul>
     *
     * <li>the specified object is also a
     * <code>TcpServerEndpoint</code>,
     *
     * <li>the host and port in the specified object are equal to the
     * host and port in this object,
     *
     * <li>either this object and the specified object both have no
     * <code>SocketFactory</code> or the <code>ServerFactory</code> in
     * the specified object has the same class and is equal to the one
     * in this object, and
     *
     * <li>either this object and the specified object both have no
     * <code>ServerSocketFactory</code> or the
     * <code>ServerSocketFactory</code> in the specified object has
     * the same class and is equal to the one in this object.
     *
     * </ul>
     *
     * @param obj the object to compare with
     *
     * @return <code>true</code> if <code>obj</code> is equivalent to
     * this object; <code>false</code> otherwise
     **/
    public boolean equals(Object obj) {
  if (obj == this) {
      return true;
  } else if (!(obj instanceof TcpServerEndpoint)) {
      return false;
  }
  TcpServerEndpoint other = (TcpServerEndpoint) obj;
  return
      Util.equals(host, other.host) &&
      port == other.port &&
      Util.sameClassAndEquals(sf, other.sf) &&
      Util.sameClassAndEquals(ssf, other.ssf);
    }

    /**
     * Returns a string representation of this
     * <code>TcpServerEndpoint</code>.
     *
     * @return a string representation of this
     * <code>TcpServerEndpoint</code>
     **/
    public String toString() {
  return "TcpServerEndpoint[" +
      (host != null ? host + ":" : "") + port +
      (ssf != null ? "," + ssf : "") +
      (sf != null ? "," + sf : "") + "]";
    }

    /**
     * ListenEndpoint implementation.
     **/
    private class LE implements ListenEndpoint {

  LE() { }

  public void checkPermissions() {
      SecurityManager sm = System.getSecurityManager();
      if (sm != null) {
    sm.checkListen(port);
      }
  }

  public ListenHandle listen(RequestDispatcher requestDispatcher)
      throws IOException
  {
      if (requestDispatcher == null) {
    throw new NullPointerException();
      }

      ServerSocket serverSocket;
      if (ssf != null) {
    serverSocket = ssf.createServerSocket(port);
      } else {
    if (useNIO) {
        serverSocket = ServerSocketChannel.open().socket();
        serverSocket.bind(new InetSocketAddress(port));
    } else {
        serverSocket = new ServerSocket(port);
    }
      }

      if (logger.isLoggable(Level.FINE)) {
    logger.log(Level.FINE,
         (ssf == null ? "created server socket {0}" :
          "created server socket {0} using factory {1}"),
         new Object[] { serverSocket, ssf });
      }

      Cookie cookie = new Cookie(serverSocket.getLocalPort());
      final LH listenHandle = new LH(requestDispatcher, serverSocket,
             Security.getContext(), cookie);
      listenHandle.startAccepting();
      return listenHandle;
  }

  // following are required to implement equals:
  private int getPort() { return port; }
  private ServerSocketFactory getSSF() { return ssf; }

  public int hashCode() {
      return port ^ (ssf != null ? ssf.hashCode() : 0);
  }

  public boolean equals(Object obj) {
      if (obj == this) {
    return true;
      } else if (!(obj instanceof LE)) {
    return false;
      }
      LE other = (LE) obj;
      return port == other.getPort() &&
    Util.sameClassAndEquals(ssf, other.getSSF());
  }

  public String toString() {
      return "TcpServerEndpoint.LE[" + port +
    (ssf != null ? "," + ssf : "") + "]";
  }

  /**
   * ListenCookie implementation: identifies a listen operation
   * by containing the local port that the server socket is
   * bound to.
   **/
  private class Cookie implements ListenCookie {

      private final int port;

      Cookie(int port) { this.port = port; }

      LE getLE() { return LE.this; }
      int getPort() { return port; }

      public String toString() {
    return "TcpServerEndpoint.LE.Cookie[" + port + "]";
      }
  }
    }

    /**
     * ListenHandle implementation: represents a listen operation.
     **/
    private static class LH implements ListenHandle {

  private final RequestDispatcher requestDispatcher;
  private final ServerSocket serverSocket;
  private final SecurityContext securityContext;
  private final ListenCookie cookie;

  private long acceptFailureTime = 0L// local to accept thread
  private int acceptFailureCount;    // local to accept thread

  private final Object lock = new Object();
  private boolean closed = false;
  private final Set connections = new HashSet();

  LH(RequestDispatcher requestDispatcher,
     ServerSocket serverSocket,
     SecurityContext securityContext,
     ListenCookie cookie)
  {
      this.requestDispatcher = requestDispatcher;
      this.serverSocket = serverSocket;
      this.securityContext = securityContext;
      this.cookie = cookie;
  }

  /**
   * Starts the accept loop.
   **/
  void startAccepting() {
      systemThreadPool.execute(new Runnable() {
    public void run() {
        try {
      executeAcceptLoop();
        } finally {
      /*
       * The accept loop is only started once, so
       * after no more socket accepts will occur,
       * ensure that the server socket is no longer
       * listening.
       */
      try {
          serverSocket.close();
      } catch (IOException e) {
      }
        }
    }
      }, toString() + " accept loop");
  }

  /**
   * Executes the accept loop.
   *
   * The accept loop runs with the full privileges of this code;
   * "accept" SocketPermissions are checked against the direct
   * user of this endpoint later, when the
   * ServerConnectionManager calls checkPermissions on the
   * InboundRequest before passing it to the RequestDispatcher.
   **/
  private void executeAcceptLoop() {
      while (true) {
    Socket socket = null;
    try {
        socket = serverSocket.accept();
        if (logger.isLoggable(Level.FINE)) {
      logger.log(Level.FINE,
          "accepted socket {0} from server socket {1}",
          new Object[]{ socket, serverSocket });
        }

        setSocketOptions(socket);

        final ServerConnection serverConnection =
      new ServerConnectionImpl(socket);

        AccessController.doPrivileged(securityContext.wrap(
      new PrivilegedAction() {
          public Object run() {
        serverConnectionManager.handleConnection(
            serverConnection, requestDispatcher);
        return null;
          }
      }), securityContext.getAccessControlContext());

    } catch (Throwable t) {
        try {
      /*
       * If this listen operation has been stopped,
       * then we expect the socket accept to throw
       * an exception, so just terminate normally.
       */
      synchronized (lock) {
          if (closed) {
        break;
          }
      }

      try {
          if (logger.isLoggable(Level.WARNING)) {
        LogUtil.logThrow(logger, Level.WARNING,
            TcpServerEndpoint.class,
            "executeAcceptLoop",
            "accept loop for {0} throws",
            new Object[] { serverSocket }, t);
          }
      } catch (Throwable tt) {
      }
        } finally {
      /*
       * Always close the accepted socket (if any)
       * if an exception occurs, but only after
       * logging an unexpected exception.
       */
      if (socket != null) {
          try {
        socket.close();
          } catch (IOException e) {
          }
      }
        }
       
        if (!(t instanceof SecurityException)) {
      try {
          // NYI: shed idle connections
      } catch (OutOfMemoryError e) {
      } catch (Exception e) {
      }
        }

        /*
         * A NoClassDefFoundError can occur if no file
         * descriptors are available, in which case this
         * loop should not terminate.
         */
        boolean knownFailure =
      t instanceof Exception ||
      t instanceof OutOfMemoryError ||
      t instanceof NoClassDefFoundError;
        if (knownFailure) {
      if (continueAfterAcceptFailure(t)) {
          continue;
      } else {
          return;
      }
        }

        throw (Error) t;
    }
      }
  }

  /**
   * Stops this listen operation.
   **/
  public void close() {
      synchronized (lock) {
    if (closed) {
        return;
    }
    closed = true;
      }

      try {
    serverSocket.close();
      } catch (IOException e) {
      }
      if (logger.isLoggable(Level.FINE)) {
    logger.log(Level.FINE,
         "closed server socket {0}", serverSocket);
      }

      /*
       * Iterating over connections without synchronization is
       * safe at this point because no other thread will access
       * it without verifying that closed is false in a
       * synchronized block first.
       */
      for (Iterator i = connections.iterator(); i.hasNext();) {
    ((ServerConnectionImpl) i.next()).close();
      }
  }

  /**
   * Returns a cookie to identify this listen operation.
   **/
  public ListenCookie getCookie() {
      return cookie;
  }

  public String toString() {
      return "TcpServerEndpoint.LH[" + serverSocket + "]";
  }

  /**
   * Throttles the accept loop after ServerSocket.accept throws
   * an exception, and decides whether to continue at all.  The
   * current code is borrowed from the JRMP implementation; it
   * always continues, but it delays the loop after bursts of
   * failed accepts.
   **/
  private boolean continueAfterAcceptFailure(Throwable t) {
      /*
       * If we get a burst of NFAIL failures in NMSEC milliseconds,
       * then wait for ten seconds.  This is to ensure that individual
       * failures don't cause hiccups, but sustained failures don't
       * hog the CPU in futile accept-fail-retry looping.
       */
      final int NFAIL = 10;
      final int NMSEC = 5000;
      long now = System.currentTimeMillis();
      if (acceptFailureTime == 0L ||
    (now - acceptFailureTime) > NMSEC)
      {
    // failure time is very old, or this is first failure
    acceptFailureTime = now;
    acceptFailureCount = 0;
      } else {
    // failure window was started recently
    acceptFailureCount++;
    if (acceptFailureCount >= NFAIL) {
        try {
      Thread.sleep(10000);
        } catch (InterruptedException ignore) {
        }
        // no need to reset counter/timer
    }
      }
      return true;
  }

  /**
   * ServerConnection implementation.
   *
   * Instances of this class should never get exposed to
   * anything other than our ServerConnectionManager, which we
   * trust to operate correctly, so we do not bother to validate
   * request handles passed in.
   **/
  private class ServerConnectionImpl implements ServerConnection {

      private final Socket socket;

      // socket attributes cached to work around 4720952:
      private final InetAddress socketInetAddress;
      private final int socketPort;

      ServerConnectionImpl(Socket socket) {
    this.socket = socket;
    socketInetAddress = socket.getInetAddress();
    socketPort = socket.getPort();
    addToConnectionSet();
      }

      public InputStream getInputStream() throws IOException {
    return socket.getInputStream();
      }

      public OutputStream getOutputStream() throws IOException {
    return socket.getOutputStream();
      }

      public SocketChannel getChannel() {
    return socket.getChannel();
      }

      public InboundRequestHandle processRequestData(InputStream in,
                 OutputStream out)
      {
    return new InboundRequestHandle() { };
      }

      public void checkPermissions(InboundRequestHandle handle) {
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        sm.checkAccept(socketInetAddress.getHostAddress(),
           socketPort);
    }
      }

      public InvocationConstraints checkConstraints(
    InboundRequestHandle handle,
    InvocationConstraints constraints)
    throws UnsupportedConstraintException
      {
    /*
     * The transport layer aspects of all constraints
     * supported by this transport provider are always
     * satisfied by all requests on the server side, so
     * this method can have the same static behavior as
     * ServerCapabilities.checkConstraints does.
     * (Otherwise, this operation would need to be
     * parameterized by this connection or the request.)
     */
    return Constraints.check(constraints, true);
      }

          public void populateContext(InboundRequestHandle handle,
          Collection context)
      {
    Util.populateContext(context, socketInetAddress);
      }

      public void close() {
    try {
        socket.close()// this will bring mux down too
    } catch (IOException e) {
    }
    if (logger.isLoggable(Level.FINE)) {
        logger.log(Level.FINE, "closed socket {0}", socket);
    }

    synchronized (lock) {
        if (!closed) {  // must not mutate set after closed
      connections.remove(this);
        }
    }
      }

      private void addToConnectionSet() {
    boolean needClose = false;
    synchronized (lock) {
        if (closed) {
      needClose = true; // close after releasing lock
        } else {
      connections.add(this);
        }
    }
    if (needClose) {
        close();
    }
      }
  }
    }

    /**
     * Attempts to set desired socket options for a connected socket
     * (TCP_NODELAY and SO_KEEPALIVE); ignores SocketException.
     **/
    private static void setSocketOptions(Socket socket) {
  try {
      socket.setTcpNoDelay(true);
  } catch (SocketException e) {
      if (logger.isLoggable(Levels.HANDLED)) {
    LogUtil.logThrow(logger, Levels.HANDLED,
         TcpServerEndpoint.class, "setSocketOptions",
         "exception setting TCP_NODELAY on socket {0}",
         new Object[] { socket }, e);
      }
  }
  try {
      socket.setKeepAlive(true);
  } catch (SocketException e) {
      if (logger.isLoggable(Levels.HANDLED)) {
    LogUtil.logThrow(logger, Levels.HANDLED,
         TcpServerEndpoint.class, "setSocketOptions",
        "exception setting SO_KEEPALIVE on socket {0}",
         new Object[] { socket }, e);
      }
  }
    }
}
TOP

Related Classes of net.jini.jeri.tcp.TcpServerEndpoint$LE

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.