Class FileDescriptorCast

java.lang.Object
org.newsclub.net.unix.FileDescriptorCast
All Implemented Interfaces:
FileDescriptorAccess

public final class FileDescriptorCast extends Object implements FileDescriptorAccess
Provides object-oriented access to file descriptors via InputStream, Socket, etc., depending on the file descriptor type.

Typical usage:


 FileDescriptor fd;

 // succeeds if fd refers to an AF_UNIX stream socket
 AFUNIXSocket socket = FileDescriptorCast.using(fd).as(AFUNIXSocket.class);

 // succeeds if fd refers to an AF_UNIX datagram socket
 AFUNIXDatagramChannel channel = FileDescriptorCast.using(fd).as(AFUNIXDatagramChannel.class);

 // always succeeds
 InputStream in = FileDescriptorCast.using(fd).as(InputStream.class);
 OutputStream in = FileDescriptorCast.using(fd).as(OutputStream.class);
 

Important notes

  1. On some platforms (e.g., Solaris, Illumos) you may need to re-apply a read timeout (e.g., using Socket.setSoTimeout(int)) after obtaining the socket.
  2. You may lose Java port information for AFSocketAddress implementations that do not encode this information directly (such as AFUNIXSocketAddress and AFTIPCSocketAddress).
  3. The "blocking" state of a socket may be forcibly changed to "blocking" when performing the cast, especially when casting to Socket, DatagramSocket or ServerSocket and any of their subclasses where "blocking" is the expected state.
  4. When calling using(FileDescriptor) for a FileDescriptor obtained from another socket or other resource in the same JVM (i.e., not from another process), especially for sockets provided by junixsocket itself, there is a chance that the garbage collector may clean up the original socket at an opportune moment, thereby closing the resource underlying the shared FileDescriptor prematurely.

    This is considered an edge-case, and deliberately not handled automatically for performance and portability reasons: We would have to do additional reference counting on all FileDescriptor instances, either through patching FileCleanable or a shared data structure.

    The issue can be prevented by keeping a reference to the original object, such as keeping it in an enclosing try-with-resources block or as a member variable, for example. Alternatively, using a "duplicate" file descriptor (via duplicating(FileDescriptor)) circumvents this problem, at the cost of using additional system resources.

  5. As a consequence of the previous point: For using(FileDescriptor): when casting file descriptors that belong to a junixsocket-controlled sockets, the target socket is configured in a way such that garbage collection will not automatically close the target's underlying file descriptor (but still potentially any file descriptors received from other processes via ancillary messages).
  6. The same restrictions as for using(FileDescriptor) apply to unsafeUsing(int) as well.
Author:
Christian Kohlschütter
  • Method Details

    • using

      public static FileDescriptorCast using(FileDescriptor fdObj) throws IOException
      Creates a FileDescriptorCast using the given file descriptor.

      Note that if any resource that also references this FileDescriptor is garbage-collected, the cleanup for that object may close the referenced FileDescriptor, thereby resulting in premature connection losses, etc. See duplicating(FileDescriptor) for a solution to this problem.

      Parameters:
      fdObj - The file descriptor.
      Returns:
      The FileDescriptorCast instance.
      Throws:
      IOException - on error, especially if the given file descriptor is invalid or unsupported.
    • duplicating

      public static FileDescriptorCast duplicating(FileDescriptor fdObj) throws IOException
      Creates a FileDescriptorCast using a duplicate of the given file descriptor.

      Duplicating a file descriptor is performed at the system-level, which means an additional file descriptor pointing to the same resource as the original is created by the operating system.

      The advantage of using duplicating(FileDescriptor) over using(FileDescriptor) is that neither implicit garbage collection nor an explicit call to Closeable.close() on a resource owning the original FileDescriptor affects the availability of the resource from the target of the cast.

      Parameters:
      fdObj - The file descriptor to duplicate.
      Returns:
      The FileDescriptorCast instance.
      Throws:
      IOException - on error, especially if the given file descriptor is invalid or unsupported, or if duplicating fails or is unsupported.
    • unsafeUsing

      public static FileDescriptorCast unsafeUsing(int fd) throws IOException
      Creates a FileDescriptorCast using the given native file descriptor value.

      This method is inherently unsafe as it may

      1. make assumptions on the internal system representation of a file descriptor (which differs between Windows and Unix, for example).
      2. provide access to resources that are otherwise not accessible

      Note that attempts are made to reuse FileDescriptor.in, FileDescriptor.out, and FileDescriptor.err, respectively.

      Parameters:
      fd - The system-native file descriptor value.
      Returns:
      The FileDescriptorCast instance.
      Throws:
      IOException - on error, especially if the given file descriptor is invalid or unsupported, or when "unsafe" operations are unavailable or manually disabled for the current environment.
    • withLocalPort

      public FileDescriptorCast withLocalPort(int port)
      Registers the given port number as the "local port" for this file descriptor. Important: This only changes the state of this instance. The actual file descriptor is not affected.
      Parameters:
      port - The port to assign to (must be >= 0).
      Returns:
      This instance.
    • withRemotePort

      public FileDescriptorCast withRemotePort(int port)
      Registers the given port number as the "remote port" for this file descriptor. Important: This only changes the state of this instance. The actual file descriptor is not affected.
      Parameters:
      port - The port to assign to (must be >= 0).
      Returns:
      This instance.
    • as

      public <K> @NonNull K as(Class<K> desiredType) throws IOException
      Casts this instance to the desired type.
      Type Parameters:
      K - The desired type.
      Parameters:
      desiredType - The class of the desired type.
      Returns:
      s An instance of the desired type.
      Throws:
      IOException - if there was a problem while casting.
      ClassCastException - if the cast cannot be legally made.
      See Also:
    • isAvailable

      public boolean isAvailable(Class<?> desiredType) throws IOException
      Checks if the instance can be cast as the given desired type (using as(Class)).
      Parameters:
      desiredType - The class of the desired type.
      Returns:
      true if the cast can be made.
      Throws:
      IOException - on error.
      See Also:
    • availableTypes

      public Set<Class<?>> availableTypes()
      Returns a collection of available types this instance can be cast to (using as(Class)).
      Returns:
      The collection of available types.
    • getFileDescriptor

      public FileDescriptor getFileDescriptor()
      Description copied from interface: FileDescriptorAccess
      Returns the corresponding FileDescriptor.
      Specified by:
      getFileDescriptor in interface FileDescriptorAccess
      Returns:
      The corresponding FileDescriptor.