Class PortManager


  • public class PortManager
    extends java.lang.Object
    Manages TCP port reservation/de-reservation while attempting to avoid intra- and extra-JVM interference.

    The port manager maintains two levels of reservation -- intra-JVM using an internal BitSet and inter-JVM using locks against a RandomAccessFile allocated in a system-wide, world-writable, system-dependent location. To avoid reservation/use races, ephemeral ports are not used.

    All users of PortManager in a JVM must use the same PortManager instance to ensure proper intra-JVM reservations.

    By default, release of an assigned port back to PortManager (via PortManager.PortRef.close() or garbage collection of a weakly-reachable PortRef instance) performs a check that the port is actually unused. This involves querying both the in-use ports and active processes on the system and, on some systems, could be a time-consuming activity. While the check is intended to identify "outside" interference with ports assigned by PortManager, it is possible to disable this check by setting the "org.terracotta.disablePortReleaseCheck" property or "DISABLE_PORT_RELEASE_CHECK" environment variable to true. The property value is examined each time the release check is performed so the check may be disabled for some tests and left enabled for others; the environment variable is only examined when the PortManger class is loaded.

    • Field Detail

      • DISABLE_PORT_RELEASE_CHECK_PROPERTY

        public static final java.lang.String DISABLE_PORT_RELEASE_CHECK_PROPERTY
        Property checked for disabling the port release check performed at the time a port obtained from PortManager is returned to PortManager.
        See Also:
        Constant Field Values
      • DISABLE_PORT_RELEASE_CHECK_ENV_VARIABLE

        public static final java.lang.String DISABLE_PORT_RELEASE_CHECK_ENV_VARIABLE
        Environment variable checked for disabling the port release check performed at the time a port obtained from PortManager is returned to PortManger.
        See Also:
        Constant Field Values
      • LOGGER

        private static final org.slf4j.Logger LOGGER
      • DISABLE_PORT_RELEASE_CHECK

        private static final boolean DISABLE_PORT_RELEASE_CHECK
        Reflects the value of the "DISABLE_PORT_RELEASE_CHECK" environment variable. If the environment variable is set to a case-insensitive value of true, the port release check will be disabled.

        The value of the environment variable is only checked when this class is loaded; changes to the environment variable after this class is loaded are not detected.

      • MINIMUM_ASSIGNABLE_PORT_COUNT

        private static final int MINIMUM_ASSIGNABLE_PORT_COUNT
        See Also:
        Constant Field Values
      • LOCALHOST

        private static final java.net.InetAddress LOCALHOST
      • rnd

        private final java.util.Random rnd
      • restrictedPorts

        private final java.util.BitSet restrictedPorts
        Marks the ports which cannot be allocated by this class. A true bit in this BitSet indicates a non-reservable (restricted) port.
      • portMap

        private final java.util.BitSet portMap
        Port allocation map. A zero bit represents an available port.
      • dereferencedPorts

        private final java.lang.ref.ReferenceQueue<PortManager.PortRef> dereferencedPorts
        A ReferenceQueue accepting PortManager.PortRef instances no longer held by the reserving party.
      • systemLevelLocker

        private final SystemLevelLocker systemLevelLocker
        Manages port reservations at a system level using file locks.
      • assignablePortCount

        private final int assignablePortCount
        The number of non-reserved ports. This is the absolute upper limit of the number of ports that can possibly be assigned.
    • Constructor Detail

      • PortManager

        private PortManager()
        Creates a PortManager instance primed for reserving ports outside of current system's the ephemeral port range.
    • Method Detail

      • getInstance

        public static PortManager getInstance()
        Gets the singleton instance of PortManager to use in a JVM.
        Returns:
        the singleton PortManager instance
      • isReservablePort

        public boolean isReservablePort​(int port)
        Indicates if the designated port is in the range of ports that may be allocated by this class. If the designated port is in the range of ports that may be allocated, the reserve(int) method may be used to attempt to allocate the port. The port may also be returned from the reservePort() and reservePorts(int) methods.
        Parameters:
        port - the port number to test
        Returns:
        true if port may be reserved using reserve(int); false if port is not a reservable port. A true result is not an indication that port is currently available to be reserved.
        Throws:
        java.lang.IllegalArgumentException - if port is not between 0 and 65535 (inclusive)
      • getPortRef

        public java.util.Optional<PortManager.PortRef> getPortRef​(int port)
        Gets the active PortManager.PortRef instance for the designated port.

        This method returns a reference to the most recent PortRef created for the designated port if the PortRef is both strongly reachable and not closed. The result of this method may be immediately stale -- the PortRef may be closed between the time the PortRef is checked and the reference returned to the called.

        Parameters:
        port - the port number for which the PortRef is returned
        Returns:
        an Optional of the PortRef for port if the PortRef is both strongly reachable and not closed; Optional.empty if the PortRef for port is either not strongly reachable or closed
        Throws:
        java.lang.IllegalArgumentException - if port is not between 0 and 65535 (inclusive) or is not a reservable port
      • reserve

        public java.util.Optional<PortManager.PortRef> reserve​(int port)
        Attempts to reserve the specified port.

        The PortManager.PortRef.close() method should be called when the port is no longer needed. However, the returned PortRef instance must strongly-referenced as long as the port is required -- once the PortRef instance becomes weakly-referenced, the port reservation may be released.

        Parameters:
        port - the port number to reserve
        Returns:
        an Optional containing the PortManager.PortRef if the port was successfully reserved; Optional.empty if the port could not be reserved
        Throws:
        java.lang.IllegalArgumentException - if port is not between 0 and 65535 (inclusive) or is not a reservable port
        java.lang.IllegalStateException - if reservation fails due to an error
      • reservePorts

        public java.util.List<PortManager.PortRef> reservePorts​(int portCount)
        Reserves the specified number of ports returning a list of PortManager.PortRef instances for those reserved.

        The PortManager.PortRef.close() method should be called on each port when no longer needed. However, each returned PortRef instance must strongly-referenced as long as the port is required -- once the PortRef instance becomes weakly-referenced, the port reservation may be released.

        Parameters:
        portCount - the number of ports to reserve
        Returns:
        the list of PortRef instances for the reserved ports; the ports are not assigned sequentially
        Throws:
        java.lang.IllegalArgumentException - if portCount is less than 1 or greater than the number of reservable ports
        java.lang.IllegalStateException - if the reservable ports are exhausted or reservation fails due to an error
      • reservePort

        public PortManager.PortRef reservePort()
        Reserve a single, randomly selected port.

        The PortManager.PortRef.close() method should be called on the port when no longer needed. However, the returned PortRef instance must strongly-referenced as long as the port is required -- once the PortRef instance becomes weakly-referenced, the port reservation may be released.

        Returns:
        a PortRef instance identifying the reserved port
        Throws:
        java.lang.IllegalStateException - if the reservable ports are exhausted or reservation fails due to an error
      • cleanReleasedPorts

        private void cleanReleasedPorts()
        Releases PortRef instances no longer strongly held. These PortRef instances are considered available for reuse.
      • reserveInternal

        private PortManager.PortRef reserveInternal​(int candidatePort)
        Reserve and vet the specified candidate port.
        Parameters:
        candidatePort - the port to vet and reserve
        Returns:
        a new PortRef instance is the reservation was successful; null otherwise
        Throws:
        java.lang.IllegalStateException - if port reservation fails due to a locking error
      • refusesConnect

        private boolean refusesConnect​(int port)
        Determines if a client connection to the specified port is accepted or refused.

        On some versions of Windows, a port can pass the server bind test and still not really be free. According to issue MNK-5621, some Windows services, like Remote Desktop (RDP) don't establish an open listener but do respond to connection requests on their assigned port(s). A "failure to connect" is necessary to determine if the port is actually available. (This check presumes firewall rules aren't responsible for dropping the connection request -- not much we can do about that.)

        Parameters:
        port - the port to test
        Returns:
        true if the port is available (free); false otherwise
        See Also:
        Service overview and network port requirements for Windows
      • disablePortReleaseCheck

        private static void disablePortReleaseCheck()
      • emitInstanceNotification

        private static void emitInstanceNotification​(java.lang.String use)
      • combine

        private static <T,​U> java.util.function.BiConsumer<T,​U> combine​(java.util.function.BiConsumer<T,​U> a,
                                                                                    java.util.function.BiConsumer<T,​U> b)
        Combines BiConsumer instances to run in reverse sequence.
        Parameters:
        a - the second BiConsumer to run
        b - the first BiConsumer to run
        Returns:
        a new BiConsumer running b then a