11package de .gesellix .docker .client .filesocket ;
22
3- import static com .sun .jna .platform .win32 .WinBase .INVALID_HANDLE_VALUE ;
3+ import org .slf4j .Logger ;
4+ import org .slf4j .LoggerFactory ;
45
56import java .io .IOException ;
67import java .io .InputStream ;
78import java .io .OutputStream ;
89import java .net .InetAddress ;
910import java .net .InetSocketAddress ;
1011import java .net .SocketAddress ;
11- import java .util .concurrent .TimeUnit ;
12-
13- import org .slf4j .Logger ;
14- import org .slf4j .LoggerFactory ;
15-
16- import com .sun .jna .platform .win32 .WinNT ;
17-
18- import okio .BufferedSink ;
19- import okio .BufferedSource ;
20- import okio .Okio ;
21- import okio .Timeout ;
12+ import java .nio .channels .AsynchronousFileChannel ;
13+ import java .nio .channels .Channels ;
14+ import java .nio .file .FileSystemException ;
15+ import java .nio .file .Paths ;
16+ import java .nio .file .StandardOpenOption ;
17+ import java .util .concurrent .atomic .AtomicBoolean ;
2218
2319public class NamedPipeSocket extends FileSocket {
2420
2521 private static final Logger log = LoggerFactory .getLogger (NamedPipeSocket .class );
2622
27- private WinNT .HANDLE handle ;
28- private boolean connected = false ;
29- private boolean closed = false ;
30-
31- private BufferedSource source ;
32- private BufferedSink sink ;
33-
34- private final Timeout ioTimeout = new Timeout ().timeout (1000 , TimeUnit .MILLISECONDS );
23+ private AsynchronousFileByteChannel channel ;
24+ private final AtomicBoolean closed = new AtomicBoolean (false );
25+ private InputStream inputStream ;
26+ private OutputStream outputStream ;
3527
3628 @ Override
3729 public void connect (SocketAddress endpoint , int timeout ) throws IOException {
@@ -42,77 +34,75 @@ public void connect(SocketAddress endpoint, int timeout) throws IOException {
4234 InetSocketAddress inetSocketAddress = (InetSocketAddress ) endpoint ;
4335 InetAddress address = inetSocketAddress .getAddress ();
4436 String socketPath = decodeHostname (address );
45- connect (socketPath );
46- }
47-
48- void connect (String socketPath ) {
49- socketPath = socketPath .replace ("/" , "\\ " );
5037 log .debug ("connect via '{}'..." , socketPath );
5138
52- handle = NamedPipeUtils .connect (socketPath , 10_000 , 500 , 50 );
53-
54- connected = true ;
55- source = Okio .buffer (new NamedPipeSource (handle , ioTimeout ));
56- sink = Okio .buffer (new NamedPipeSink (handle , ioTimeout ));
57- }
58-
59- @ Override
60- public InputStream getInputStream () throws IOException {
61- ensureOpen ();
62- return source .inputStream ();
63- }
64-
65- @ Override
66- public OutputStream getOutputStream () throws IOException {
67- ensureOpen ();
68- return sink .outputStream ();
39+ socketPath = socketPath .replace ("/" , "\\ \\ " );
40+
41+ long startedAt = System .currentTimeMillis ();
42+ timeout = Math .max (timeout , 10_000 );
43+ while (true ) {
44+ try {
45+ channel = new AsynchronousFileByteChannel (
46+ AsynchronousFileChannel .open (
47+ Paths .get (socketPath ),
48+ StandardOpenOption .READ ,
49+ StandardOpenOption .WRITE
50+ )
51+ );
52+ break ;
53+ }
54+ catch (FileSystemException e ) {
55+ if (System .currentTimeMillis () - startedAt >= timeout ) {
56+ throw new RuntimeException (e );
57+ }
58+ else {
59+ // requires a bit more code and the net.java.dev.jna:jna dependency
60+ // Kernel32.INSTANCE.WaitNamedPipe(socketFileName, 100);
61+ try {
62+ Thread .sleep (100 );
63+ }
64+ catch (InterruptedException ignored ) {
65+ }
66+ }
67+ }
68+ }
6969 }
7070
7171 @ Override
72- public synchronized void close () throws IOException {
73- if (closed ) {
74- return ;
75- }
76- log .debug ("closing handle {}..." , handle );
77- try {
78- if (handle != null && !INVALID_HANDLE_VALUE .equals (handle )) {
79- // Cancel any pending read/write before closing to avoid CloseHandle() hang
80- ExtendedKernel32 .INSTANCE .CancelIoEx (handle , null );
81- }
82-
83- if (source != null ) {
84- log .debug ("closing source {}..." , source );
85- source .close ();
86- }
87- if (sink != null ) {
88- log .debug ("closing sink {}..." , sink );
89- sink .close ();
90- }
91- } finally {
92- if (handle != null ) {
93- NamedPipeUtils .closeHandle (handle );
94- }
95- closed = true ;
96- connected = false ;
72+ public InputStream getInputStream () {
73+ if (inputStream == null ) {
74+ this .inputStream = Channels .newInputStream (channel );
9775 }
76+ return inputStream ;
9877 }
9978
10079 @ Override
101- public boolean isConnected () {
102- return connected ;
80+ public OutputStream getOutputStream () {
81+ if (outputStream == null ) {
82+ this .outputStream = Channels .newOutputStream (channel );
83+ }
84+ return outputStream ;
10385 }
10486
10587 @ Override
10688 public boolean isClosed () {
107- return closed ;
89+ return closed . get () ;
10890 }
10991
110- private void ensureOpen () throws IOException {
111- if (closed ) {
112- throw new IOException ("NamedPipeSocket is closed" );
92+ @ Override
93+ public void close () throws IOException {
94+ if (!closed .compareAndSet (false , true )) {
95+ // if compareAndSet() returns false closed was already true
96+ return ;
97+ }
98+ if (channel != null ) {
99+ channel .close ();
100+ }
101+ if (inputStream != null ) {
102+ inputStream .close ();
113103 }
114- if (! connected ) {
115- throw new IOException ( "NamedPipeSocket is not connected" );
104+ if (outputStream != null ) {
105+ outputStream . close ( );
116106 }
117107 }
118108}
0 commit comments