Class PortableSocket
java.lang.Object
com.clumd.projects.java_common_utils.base_enhancements.PortableSocket
- All Implemented Interfaces:
AutoCloseable
This
Socket wrapper serves to accumulate some convenience methods for dealing with sockets, such as setting up ObjectStreams,
setting timeouts and preparing customised ClassLoaders for interpreting incoming objects. It is also called 'Portable' as THIS is the object
reference which should be passed around to represent a socket endpoint, rather than the socket itself and create multiple input/output streams on.
One other extremely useful feature it provides is a PORTABLE method of determining a connection's reachability.
As per the
Java documentation
and countless stackoverflow posts, the baked in InetAddress.isReachable(int) will only make best-effort to perform a regular ICMP PING
request. However, certain operating systems may only allow these calls if the JVM is running with ROOT/ADMIN privileges. This implementation
(portableIsReachable(String, int))
seeks to improve this, by going a few steps further to determine reachability such as actually trying to construct a Socket to the peer on a
known, provided, port.
-
Constructor Summary
ConstructorsConstructorDescriptionPortableSocket(@NonNull String hostname, int port, Serializable... requiredSocketStreamHeaderContent) This constructor accepts a hostname and port which we would like to connect to, and potentially, a collection of objects which will be written and expected to be read as a stream header.PortableSocket(@NonNull Socket socket, Serializable... requiredSocketStreamHeaderContent) This constructor accepts a *connected* Socket, and potentially, a collection of objects which will be written and expected to be read as a stream header. -
Method Summary
Modifier and TypeMethodDescriptionvoidclose()Used to acquire the reference to this Portable Socket's INPUT stream.Used to acquire the reference to this Portable Socket's OUTPUT stream.initialiseInputStreamWithComponentLoader(@NonNull URLClassLoader componentLoaderForInputStream) Used to initialise the InputStream for this PortableSocket using a custom provided URLClassLoader, such that it can interpret the kinds of new Objects which this PortableSocket will be used to receive.booleanisClosed()Simple pass-through method to the underlyingSocket.isClosed().static booleanisLocalHostname(@NonNull String hostname) Used to determine whether a given hostname is local to the system which is currently running this code.static booleanportableIsReachable(@NonNull String hostname, int port) This method should act as an enhanced and more reliable version of Java's built inInetAddress.isReachable(int).intsetCustomTimeoutInMs(int timeoutInMilliseconds) Calls through toSocket.setSoTimeout(int)with a custom provided value.intCalls through toSocket.setSoTimeout(int)with a default 'short' value.intCalls through toSocket.setSoTimeout(int)with a default 'medium' value.intCalls through toSocket.setSoTimeout(int)with a default 'long' value.intCalls through toSocket.setSoTimeout(int)with a default 'medium' value.intCalls through toSocket.setSoTimeout(int)with a value of 0, which is interpreted as limitless.intCalls through toSocket.setSoTimeout(int)with a default 'short' value.intCalls through toSocket.setSoTimeout(int)with a default 'long' value.intCalls through toSocket.setSoTimeout(int)with a default 'long' value.intCalls through toSocket.setSoTimeout(int)with a default 'medium' value.intCalls through toSocket.setSoTimeout(int)with a default 'short' value.
-
Constructor Details
-
PortableSocket
public PortableSocket(@NonNull @NonNull Socket socket, Serializable... requiredSocketStreamHeaderContent) throws IOException This constructor accepts a *connected* Socket, and potentially, a collection of objects which will be written and expected to be read as a stream header. This allows both sides of a PortableSocket to match each other reliably. This constructor will create an OutputStream for this socket, will set a Medium timeout, but will NOT immediately create an InputStream. This is to allow a caller to potentially provide a custom URL ClassLoader throughinitialiseInputStreamWithComponentLoader(URLClassLoader).- Parameters:
socket- The already connected low level Socket. If a socket is provided but is not connected, then you can't use a portable socket yet as, to cope with this, involves having to then keep track of all the is initialised and connected etc. Basically I'm just a bit lazy, but I don't think it makes sense for this class.requiredSocketStreamHeaderContent- Variadic args for what to write down the socket on connection, and expect to read from the socket on connection. Passing nothing and treating this constructor as if this variadic did not exist, will leave the DEFAULT (In/Out)putStream header behaviour in place. Passing a type-casted(Object[]) null, will disable any read/write operations within the streams initialisations. Passing any other variadic data, will write those objects in order and expect to read the same from the other side on initialisation.- Throws:
IOException- This can be thrown if there was an issue creating the output streams, or defaulting the socket operation timeouts.
-
PortableSocket
public PortableSocket(@NonNull @NonNull String hostname, int port, Serializable... requiredSocketStreamHeaderContent) throws IOException This constructor accepts a hostname and port which we would like to connect to, and potentially, a collection of objects which will be written and expected to be read as a stream header. This allows both sides of a PortableSocket to match each other reliably. If this constructor fails to produce a successful connection to the hostname and port provided and exception will be thrown. This constructor will always attempt to connect with a medium timeout, create an OutputStream for this socket and set a Medium timeout on future operations, but will NOT immediately create an InputStream. This is to allow a caller to potentially provide a custom URL ClassLoader throughinitialiseInputStreamWithComponentLoader(URLClassLoader).- Parameters:
hostname- The network hostname or IP of the peer we would like to connect to.port- The port number we would like to initialise a connection on.requiredSocketStreamHeaderContent- Variadic args for what to write down the socket on connection, and expect to read from the socket on connection. Passing nothing and treating this constructor as if this variadic did not exist, will leave the DEFAULT (In/Out)putStream header behaviour in place. Passing a type-casted(Object[]) null, will disable any read/write operations within the streams initialisations. Passing any other variadic data, will write those objects in order and expect to read the same from the other side on initialisation.- Throws:
IOException- This can be thrown if there was an issue creating the output streams, or defaulting the socket operation timeouts.
-
-
Method Details
-
portableIsReachable
public static boolean portableIsReachable(@NonNull @NonNull String hostname, int port) throws IOException This method should act as an enhanced and more reliable version of Java's built inInetAddress.isReachable(int). This is because - as is widely documented - in the Java documentation and countless stackoverflow posts, the baked inInetAddress.isReachable(int)will only make best-effort to perform a regular ICMP PING request. However, certain operating systems may only allow these calls if the JVM is running with ROOT/ADMIN privileges, which on most Windows and MacOS systems it won't be by default. This implementation goes the step further by first calling thisInetAddress.isReachable(int), but if it fails, actually attempting to create the connection - which follows a different workflow in the JVM network stack regarding the permissions it asks for, (I.e. NOT asking the system to perform ICMP control operations). This method of checking can validate both address resolution, as well as actual route traversal to the remote host.- Parameters:
hostname- The hostname of the host we would like to check connectivity to.port- The port on that hostname which we would like to check connectivity to.- Returns:
- True if the host is reachable, False otherwise.
- Throws:
IOException- Thrown if there was an error setting up the Sockets, a timeout occurred, or the host was otherwise unreachable.
-
isLocalHostname
Used to determine whether a given hostname is local to the system which is currently running this code.- Parameters:
hostname- The String to check whether it is a locally addressable hostname- Returns:
- True if the String provided is a local hostname, False otherwise.
- Throws:
IOException- Thrown if there was some networking stack issue in determining whether this hostname is local, or where the string provided could never be a valid hostname.
-
initialiseInputStreamWithComponentLoader
public ObjectInputStream initialiseInputStreamWithComponentLoader(@NonNull @NonNull URLClassLoader componentLoaderForInputStream) throws IOException Used to initialise the InputStream for this PortableSocket using a custom provided URLClassLoader, such that it can interpret the kinds of new Objects which this PortableSocket will be used to receive. This method should only ever be called once per PortableSocket, and must be called before any calls togetInputStream(), as that will attempt default initialisation (without your custom URL Classloader) if the Stream is not already initialised. If this method is being called for the first time initialisation, then any potential Stream Header information provided to the Constructor of this PortableSocket, will attempt to be READ on initialisation before successfully returning the Stream back to the caller.- Parameters:
componentLoaderForInputStream- The URLClassLoader which the InputStream for this PortableSocket should use when parsing data back into Objects.- Returns:
- The successfully initialised InputStream ready to be read from.
- Throws:
IOException- Thrown if the Stream has already been initialised elsewhere, or if any potential Stream Header information was not read as expected.
-
getOutputStream
Used to acquire the reference to this Portable Socket's OUTPUT stream. Only one output stream is created per PortableSocket.- Returns:
- The OutputStream associated with this PortableSocket.
- Throws:
IOException- Thrown if this was the first time this method was called since constructing the Portable Socket, and there was an error setting up the OutputStream - such as the Socket being immediately closed, or being unable to write an uninterrupted Stream Header to the remote end of the Socket.
-
getInputStream
Used to acquire the reference to this Portable Socket's INPUT stream. Only one input stream is created per PortableSocket.- Returns:
- The InputStream associated with this PortableSocket.
- Throws:
IOException- Thrown if this was the first time this method was called since constructing the Portable Socket, and there was an error setting up the InputStream - such as the Socket being immediately closed, or reading a Corrupt Stream Header from the remote end of the Socket.
-
isClosed
public boolean isClosed()Simple pass-through method to the underlyingSocket.isClosed().- Returns:
- Whether the socket this PortableSocket is built on, reports itself as closed.
-
close
public void close()- Specified by:
closein interfaceAutoCloseable
-
setFastTimeout
Calls through toSocket.setSoTimeout(int)with a default 'short' value.- Returns:
- The previous value which would have been returned by
Socket.getSoTimeout()before calling this method. - Throws:
SocketException- Can be thrown for example if the Socket you are trying to set timeouts on, is closed.
-
setMediumTimeout
Calls through toSocket.setSoTimeout(int)with a default 'medium' value.- Returns:
- The previous value which would have been returned by
Socket.getSoTimeout()before calling this method. - Throws:
SocketException- Can be thrown for example if the Socket you are trying to set timeouts on, is closed.
-
setLongTimeout
Calls through toSocket.setSoTimeout(int)with a default 'long' value.- Returns:
- The previous value which would have been returned by
Socket.getSoTimeout()before calling this method. - Throws:
SocketException- Can be thrown for example if the Socket you are trying to set timeouts on, is closed.
-
setOneSecondTimeout
Calls through toSocket.setSoTimeout(int)with a default 'short' value.- Returns:
- The previous value which would have been returned by
Socket.getSoTimeout()before calling this method. - Throws:
SocketException- Can be thrown for example if the Socket you are trying to set timeouts on, is closed.
-
setTwoSecondTimeout
Calls through toSocket.setSoTimeout(int)with a default 'short' value.- Returns:
- The previous value which would have been returned by
Socket.getSoTimeout()before calling this method. - Throws:
SocketException- Can be thrown for example if the Socket you are trying to set timeouts on, is closed.
-
setThreeSecondTimeout
Calls through toSocket.setSoTimeout(int)with a default 'medium' value.- Returns:
- The previous value which would have been returned by
Socket.getSoTimeout()before calling this method. - Throws:
SocketException- Can be thrown for example if the Socket you are trying to set timeouts on, is closed.
-
setFiveSecondTimeout
Calls through toSocket.setSoTimeout(int)with a default 'medium' value.- Returns:
- The previous value which would have been returned by
Socket.getSoTimeout()before calling this method. - Throws:
SocketException- Can be thrown for example if the Socket you are trying to set timeouts on, is closed.
-
setTenSecondTimeout
Calls through toSocket.setSoTimeout(int)with a default 'long' value.- Returns:
- The previous value which would have been returned by
Socket.getSoTimeout()before calling this method. - Throws:
SocketException- Can be thrown for example if the Socket you are trying to set timeouts on, is closed.
-
setThirtySecondTimeout
Calls through toSocket.setSoTimeout(int)with a default 'long' value.- Returns:
- The previous value which would have been returned by
Socket.getSoTimeout()before calling this method. - Throws:
SocketException- Can be thrown for example if the Socket you are trying to set timeouts on, is closed.
-
setNoTimeout
Calls through toSocket.setSoTimeout(int)with a value of 0, which is interpreted as limitless.- Returns:
- The previous value which would have been returned by
Socket.getSoTimeout()before calling this method. - Throws:
SocketException- Can be thrown for example if the Socket you are trying to set timeouts on, is closed.
-
setCustomTimeoutInMs
Calls through toSocket.setSoTimeout(int)with a custom provided value.- Parameters:
timeoutInMilliseconds- The amount of milliseconds which should be allowed to pass before read operations on this Socket's streams should throw timeout exceptions.- Returns:
- The previous value which would have been returned by
Socket.getSoTimeout()before calling this method. - Throws:
SocketException- Can be thrown for example if the Socket you are trying to set timeouts on, is closed.
-
getSocket
-