package com.github.dockerjava.transport;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.channels.Channels;
import java.nio.channels.SocketChannel;

public class UnixSocket extends AbstractSocket {

    /**
     * Return a new {@link Socket} for the given path. Will use JDK's {@link java.net.UnixDomainSocketAddress}
     * if available and fallback to {@link DomainSocket} otherwise.
     *
     * @param path the path to the domain socket
     * @return a {@link Socket} instance
     * @throws IOException if the socket cannot be opened
     */
    public static Socket get(String path) throws IOException {
        try {
            return new UnixSocket(path);
        } catch (Exception e) {
            //noinspection deprecation
            return DomainSocket.get(path);
        }
    }

    private final SocketAddress socketAddress;

    private final SocketChannel socketChannel;

    private UnixSocket(String path) throws Exception {
        Class<?> unixDomainSocketAddress = Class.forName("java.net.UnixDomainSocketAddress");
        this.socketAddress =
            (SocketAddress) unixDomainSocketAddress.getMethod("of", String.class)
                .invoke(null, path);
        this.socketChannel = SocketChannel.open(this.socketAddress);
    }

    @Override
    public InputStream getInputStream() throws IOException {
        if (isClosed()) {
            throw new SocketException("Socket is closed");
        }
        if (!isConnected()) {
            throw new SocketException("Socket is not connected");
        }
        if (isInputShutdown()) {
            throw new SocketException("Socket input is shutdown");
        }

        return Channels.newInputStream(socketChannel);
    }

    @Override
    public OutputStream getOutputStream() throws IOException {
        if (isClosed()) {
            throw new SocketException("Socket is closed");
        }
        if (!isConnected()) {
            throw new SocketException("Socket is not connected");
        }
        if (isOutputShutdown()) {
            throw new SocketException("Socket output is shutdown");
        }

        return Channels.newOutputStream(socketChannel);
    }

    @Override
    public SocketAddress getLocalSocketAddress() {
        return socketAddress;
    }

    @Override
    public SocketAddress getRemoteSocketAddress() {
        return socketAddress;
    }

    @Override
    public void close() throws IOException {
        super.close();
        this.socketChannel.close();
    }
}

@@ -200,32 +200,60 @@
 ocketException;%0A
+import java.nio.ByteBuffer;%0A
 import java.nio.
@@ -310,16 +310,62 @@
 Channel;
+%0Aimport java.nio.channels.WritableByteChannel;
 %0A%0Apublic
@@ -2352,37 +2352,56 @@
 utputStream(
-socket
+new WrappedWritableByte
 Channel
+()
 );%0A    %7D%0A%0A  
@@ -2681,24 +2681,24 @@
 er.close();%0A
-
         this
@@ -2726,11 +2726,499 @@
 );%0A    %7D
+%0A%0A    private class WrappedWritableByteChannel implements WritableByteChannel %7B%0A%0A        @Override%0A        public int write(ByteBuffer src) throws IOException %7B%0A            return UnixSocket.this.socketChannel.write(src);%0A        %7D%0A%0A        @Override%0A        public boolean isOpen() %7B%0A            return UnixSocket.this.socketChannel.isOpen();%0A        %7D%0A%0A        @Override%0A        public void close() throws IOException %7B%0A            UnixSocket.this.socketChannel.close();%0A        %7D%0A    %7D
 %0A%7D%0A

Use a non-blocking output stream for socket writes (#1769)