Java Unix Domain Socket implementation providing AF_UNIX, AF_TIPC, AF_VSOCK, and AF_SYSTEM socket support with traditional and NIO APIs
—
Comprehensive exception hierarchy providing detailed error information for robust socket error handling and debugging in Unix Domain Socket operations.
Base exception class for all AF socket-related errors, extending IOException to integrate with standard Java I/O exception handling.
/**
* Base exception class for AF socket operations
*/
public class AFException extends IOException {
/**
* Creates a new AFException with the specified message
* @param message The error message
*/
public AFException(String message);
/**
* Creates a new AFException with message and cause
* @param message The error message
* @param cause The underlying cause
*/
public AFException(String message, Throwable cause);
/**
* Creates a new AFException with the specified cause
* @param cause The underlying cause
*/
public AFException(Throwable cause);
// Error information
public String getMessage();
public Throwable getCause();
public String getLocalizedMessage();
// Stack trace information
public StackTraceElement[] getStackTrace();
public void printStackTrace();
public void printStackTrace(PrintStream s);
public void printStackTrace(PrintWriter s);
}Usage Examples:
import org.newsclub.net.unix.*;
try {
AFUNIXSocket socket = AFUNIXSocket.connectTo(AFUNIXSocketAddress.of("/nonexistent/path.sock"));
} catch (AFException e) {
System.err.println("AF Socket error: " + e.getMessage());
// Check for specific causes
Throwable cause = e.getCause();
if (cause instanceof FileNotFoundException) {
System.err.println("Socket file not found");
} else if (cause instanceof SecurityException) {
System.err.println("Permission denied");
}
// Log full stack trace for debugging
e.printStackTrace();
}Exceptions related to socket state and lifecycle management.
/**
* Exception thrown when operating on closed sockets
*/
public class SocketClosedException extends SocketException {
public SocketClosedException();
public SocketClosedException(String msg);
public SocketClosedException(String msg, Throwable cause);
}
/**
* Exception thrown when socket address is unavailable
*/
public class AddressUnavailableSocketException extends SocketException {
public AddressUnavailableSocketException();
public AddressUnavailableSocketException(String msg);
public AddressUnavailableSocketException(String msg, Throwable cause);
}
/**
* Exception thrown when connection is refused
*/
public class ConnectionRefusedSocketException extends SocketException {
public ConnectionRefusedSocketException();
public ConnectionRefusedSocketException(String msg);
public ConnectionRefusedSocketException(String msg, Throwable cause);
}Usage Examples:
// Socket state validation
public void performSocketOperation(AFUNIXSocket socket) throws IOException {
try {
if (socket.isClosed()) {
throw new SocketClosedException("Socket is already closed");
}
// Perform operation
OutputStream os = socket.getOutputStream();
os.write("data".getBytes());
} catch (SocketClosedException e) {
System.err.println("Cannot operate on closed socket: " + e.getMessage());
throw e;
}
}
// Address availability check
public AFUNIXServerSocket createServer(File socketFile) throws IOException {
try {
AFUNIXSocketAddress address = AFUNIXSocketAddress.of(socketFile);
AFUNIXServerSocket server = AFUNIXServerSocket.newInstance();
server.bind(address);
return server;
} catch (AddressUnavailableSocketException e) {
System.err.println("Socket address unavailable: " + socketFile);
// Try to clean up and retry
if (socketFile.exists() && socketFile.delete()) {
System.out.println("Cleaned up stale socket file, retrying...");
return createServer(socketFile);
}
throw e;
}
}
// Connection handling
public void connectWithRetry(AFUNIXSocketAddress address, int maxRetries) throws IOException {
int attempts = 0;
while (attempts < maxRetries) {
try {
AFUNIXSocket socket = AFUNIXSocket.connectTo(address);
System.out.println("Connected successfully");
return;
} catch (ConnectionRefusedSocketException e) {
attempts++;
System.err.println("Connection refused, attempt " + attempts + "/" + maxRetries);
if (attempts >= maxRetries) {
throw new IOException("Failed to connect after " + maxRetries + " attempts", e);
}
try {
Thread.sleep(1000 * attempts); // Exponential backoff
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new IOException("Connection interrupted", ie);
}
}
}
}Exceptions related to data transmission and socket communication errors.
/**
* Exception thrown when pipe is broken during communication
*/
public class BrokenPipeSocketException extends SocketException {
public BrokenPipeSocketException();
public BrokenPipeSocketException(String msg);
public BrokenPipeSocketException(String msg, Throwable cause);
}
/**
* Exception thrown when connection is reset by peer
*/
public class ConnectionResetSocketException extends SocketException {
public ConnectionResetSocketException();
public ConnectionResetSocketException(String msg);
public ConnectionResetSocketException(String msg, Throwable cause);
}
/**
* Exception thrown when socket times out
*/
public class SocketTimeoutException extends InterruptedIOException {
public SocketTimeoutException();
public SocketTimeoutException(String msg);
public SocketTimeoutException(String msg, Throwable cause);
}Usage Examples:
// Robust data transmission
public void sendDataWithErrorHandling(AFUNIXSocket socket, byte[] data) throws IOException {
try {
OutputStream os = socket.getOutputStream();
os.write(data);
os.flush();
} catch (BrokenPipeSocketException e) {
System.err.println("Broken pipe - peer closed connection: " + e.getMessage());
// Clean up resources
try {
socket.close();
} catch (IOException closeEx) {
e.addSuppressed(closeEx);
}
throw e;
} catch (ConnectionResetSocketException e) {
System.err.println("Connection reset by peer: " + e.getMessage());
// Log connection reset and clean up
logConnectionReset(socket, e);
throw e;
}
}
// Timeout handling
public String readWithTimeout(AFUNIXSocket socket, int timeoutMs) throws IOException {
try {
socket.setSoTimeout(timeoutMs);
InputStream is = socket.getInputStream();
byte[] buffer = new byte[1024];
int bytesRead = is.read(buffer);
if (bytesRead > 0) {
return new String(buffer, 0, bytesRead, StandardCharsets.UTF_8);
}
return null;
} catch (SocketTimeoutException e) {
System.err.println("Read timeout after " + timeoutMs + "ms: " + e.getMessage());
// Decide whether to retry or fail
if (shouldRetryAfterTimeout(socket)) {
return readWithTimeout(socket, timeoutMs * 2); // Double timeout and retry
}
throw e;
} finally {
// Reset timeout
socket.setSoTimeout(0);
}
}
private boolean shouldRetryAfterTimeout(AFUNIXSocket socket) {
return socket.isConnected() && !socket.isClosed();
}
private void logConnectionReset(AFUNIXSocket socket, ConnectionResetSocketException e) {
try {
AFUNIXSocketAddress localAddr = (AFUNIXSocketAddress) socket.getLocalSocketAddress();
AFUNIXSocketAddress remoteAddr = (AFUNIXSocketAddress) socket.getRemoteSocketAddress();
System.err.println("Connection reset - Local: " + localAddr + ", Remote: " + remoteAddr);
} catch (Exception ex) {
System.err.println("Connection reset - Unable to get socket addresses");
}
}Exceptions related to system-level errors and platform-specific issues.
/**
* Exception thrown when device or resource is not found
*/
public class NoSuchDeviceSocketException extends SocketException {
public NoSuchDeviceSocketException();
public NoSuchDeviceSocketException(String msg);
public NoSuchDeviceSocketException(String msg, Throwable cause);
}
/**
* Exception thrown when operation is not supported
*/
public class OperationNotSupportedSocketException extends SocketException {
public OperationNotSupportedSocketException();
public OperationNotSupportedSocketException(String msg);
public OperationNotSupportedSocketException(String msg, Throwable cause);
}
/**
* Exception thrown when invalid argument is provided
*/
public class InvalidArgumentSocketException extends SocketException {
public InvalidArgumentSocketException();
public InvalidArgumentSocketException(String msg);
public InvalidArgumentSocketException(String msg, Throwable cause);
}Usage Examples:
// Platform capability checking
public void performPlatformSpecificOperation(AFUNIXSocket socket) throws IOException {
try {
// Attempt platform-specific operation (e.g., peer credentials)
PeerCredentials credentials = socket.getPeerCredentials();
System.out.println("Peer PID: " + credentials.getPid());
} catch (OperationNotSupportedSocketException e) {
System.err.println("Peer credentials not supported on this platform: " + e.getMessage());
// Fall back to alternative approach
performAlternativeAuthentication(socket);
} catch (NoSuchDeviceSocketException e) {
System.err.println("Required device not available: " + e.getMessage());
throw new IOException("Platform requirements not met", e);
}
}
// Argument validation
public void configureSocket(AFUNIXSocket socket, Map<String, Object> options) throws IOException {
try {
for (Map.Entry<String, Object> option : options.entrySet()) {
applySocketOption(socket, option.getKey(), option.getValue());
}
} catch (InvalidArgumentSocketException e) {
System.err.println("Invalid socket configuration: " + e.getMessage());
// Log configuration details for debugging
System.err.println("Configuration options: " + options);
throw new IllegalArgumentException("Socket configuration failed", e);
}
}
private void applySocketOption(AFUNIXSocket socket, String optionName, Object value) throws IOException {
switch (optionName.toLowerCase()) {
case "timeout":
if (!(value instanceof Integer)) {
throw new InvalidArgumentSocketException("Timeout must be an integer");
}
socket.setSoTimeout((Integer) value);
break;
case "keepalive":
if (!(value instanceof Boolean)) {
throw new InvalidArgumentSocketException("KeepAlive must be a boolean");
}
socket.setKeepAlive((Boolean) value);
break;
default:
throw new InvalidArgumentSocketException("Unknown socket option: " + optionName);
}
}
private void performAlternativeAuthentication(AFUNIXSocket socket) {
// Alternative authentication mechanism
System.out.println("Using alternative authentication method");
}// Complete error handling pattern for socket operations
public class RobustSocketClient {
private static final int MAX_RETRIES = 3;
private static final int RETRY_DELAY_MS = 1000;
public void performRobustOperation(File socketFile, byte[] data) throws IOException {
AFUNIXSocketAddress address = AFUNIXSocketAddress.of(socketFile);
IOException lastException = null;
for (int attempt = 1; attempt <= MAX_RETRIES; attempt++) {
try {
performSingleOperation(address, data);
return; // Success
} catch (AddressUnavailableSocketException e) {
System.err.println("Attempt " + attempt + ": Address unavailable - " + e.getMessage());
lastException = e;
if (attempt == MAX_RETRIES) {
throw new IOException("Socket server appears to be down", e);
}
} catch (ConnectionRefusedSocketException e) {
System.err.println("Attempt " + attempt + ": Connection refused - " + e.getMessage());
lastException = e;
if (attempt == MAX_RETRIES) {
throw new IOException("Server not accepting connections", e);
}
} catch (BrokenPipeSocketException | ConnectionResetSocketException e) {
System.err.println("Attempt " + attempt + ": Connection lost - " + e.getMessage());
lastException = e;
// Don't retry on connection loss
throw new IOException("Connection lost during operation", e);
} catch (SocketTimeoutException e) {
System.err.println("Attempt " + attempt + ": Operation timed out - " + e.getMessage());
lastException = e;
if (attempt == MAX_RETRIES) {
throw new IOException("Operation timed out after " + MAX_RETRIES + " attempts", e);
}
} catch (OperationNotSupportedSocketException e) {
// Don't retry unsupported operations
throw new IOException("Operation not supported on this platform", e);
} catch (AFException e) {
System.err.println("Attempt " + attempt + ": AF socket error - " + e.getMessage());
lastException = e;
if (attempt == MAX_RETRIES) {
throw new IOException("Socket operation failed", e);
}
}
if (attempt < MAX_RETRIES) {
try {
Thread.sleep(RETRY_DELAY_MS * attempt); // Progressive delay
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new IOException("Operation interrupted", ie);
}
}
}
throw new IOException("All retry attempts failed", lastException);
}
private void performSingleOperation(AFUNIXSocketAddress address, byte[] data) throws IOException {
try (AFUNIXSocket socket = AFUNIXSocket.connectTo(address)) {
socket.setSoTimeout(5000); // 5 second timeout
// Send data
OutputStream os = socket.getOutputStream();
os.write(data);
os.flush();
// Read response
InputStream is = socket.getInputStream();
byte[] buffer = new byte[1024];
int bytesRead = is.read(buffer);
if (bytesRead > 0) {
String response = new String(buffer, 0, bytesRead, StandardCharsets.UTF_8);
System.out.println("Received: " + response);
}
}
}
}// Exception classification for different recovery strategies
public class ExceptionHandler {
public enum ErrorSeverity {
RECOVERABLE, // Can retry
TRANSIENT, // May recover after delay
PERMANENT, // Don't retry
FATAL // Should terminate
}
public static ErrorSeverity classifyException(Exception e) {
if (e instanceof SocketClosedException) {
return ErrorSeverity.PERMANENT;
} else if (e instanceof AddressUnavailableSocketException) {
return ErrorSeverity.TRANSIENT;
} else if (e instanceof ConnectionRefusedSocketException) {
return ErrorSeverity.TRANSIENT;
} else if (e instanceof BrokenPipeSocketException) {
return ErrorSeverity.PERMANENT;
} else if (e instanceof ConnectionResetSocketException) {
return ErrorSeverity.PERMANENT;
} else if (e instanceof SocketTimeoutException) {
return ErrorSeverity.RECOVERABLE;
} else if (e instanceof OperationNotSupportedSocketException) {
return ErrorSeverity.FATAL;
} else if (e instanceof InvalidArgumentSocketException) {
return ErrorSeverity.FATAL;
} else if (e instanceof AFException) {
return ErrorSeverity.TRANSIENT;
}
return ErrorSeverity.PERMANENT;
}
public static boolean shouldRetry(Exception e, int attemptCount, int maxAttempts) {
if (attemptCount >= maxAttempts) {
return false;
}
ErrorSeverity severity = classifyException(e);
return severity == ErrorSeverity.RECOVERABLE || severity == ErrorSeverity.TRANSIENT;
}
public static long getRetryDelay(Exception e, int attemptCount) {
ErrorSeverity severity = classifyException(e);
switch (severity) {
case RECOVERABLE:
return Math.min(1000L * (1L << attemptCount), 30000L); // Exponential backoff, max 30s
case TRANSIENT:
return Math.min(2000L * attemptCount, 10000L); // Linear backoff, max 10s
default:
return 0L;
}
}
}// Exception logging for monitoring and debugging
public class SocketExceptionLogger {
private static final Logger logger = LoggerFactory.getLogger(SocketExceptionLogger.class);
public static void logException(Exception e, AFUNIXSocket socket, String operation) {
Map<String, Object> context = new HashMap<>();
context.put("operation", operation);
context.put("exception_type", e.getClass().getSimpleName());
context.put("exception_message", e.getMessage());
try {
if (socket != null) {
context.put("socket_closed", socket.isClosed());
context.put("socket_connected", socket.isConnected());
context.put("socket_bound", socket.isBound());
if (socket.getLocalSocketAddress() != null) {
context.put("local_address", socket.getLocalSocketAddress().toString());
}
if (socket.getRemoteSocketAddress() != null) {
context.put("remote_address", socket.getRemoteSocketAddress().toString());
}
}
} catch (Exception ex) {
context.put("socket_info_error", ex.getMessage());
}
ExceptionHandler.ErrorSeverity severity = ExceptionHandler.classifyException(e);
context.put("error_severity", severity.toString());
// Log based on severity
switch (severity) {
case FATAL:
logger.error("Fatal socket error: {}", context, e);
break;
case PERMANENT:
logger.warn("Permanent socket error: {}", context, e);
break;
case TRANSIENT:
logger.info("Transient socket error: {}", context);
break;
case RECOVERABLE:
logger.debug("Recoverable socket error: {}", context);
break;
}
}
}Install with Tessl CLI
npx tessl i tessl/maven-com-kohlschutter-junixsocket--junixsocket-core