Skip to content

Commit 6044357

Browse files
authored
Use AsynchronousFileChannel for npipe (#1408)
* Use `AsynchronousFileChannel` for npipe * ignore `AsynchronousCloseException` on read * Replace `FileNotFoundException` with `NoSuchFileException`
1 parent 5bfb6c0 commit 6044357

File tree

3 files changed

+257
-149
lines changed

3 files changed

+257
-149
lines changed

docker-java-transport-httpclient5/src/main/java/com/github/dockerjava/httpclient5/NamedPipeSocket.java

Lines changed: 97 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4,101 +4,77 @@
44
import com.sun.jna.win32.StdCallLibrary;
55
import com.sun.jna.win32.W32APIOptions;
66

7-
import java.io.FileNotFoundException;
87
import java.io.IOException;
98
import java.io.InputStream;
109
import java.io.OutputStream;
11-
import java.io.RandomAccessFile;
1210
import java.net.Socket;
1311
import java.net.SocketAddress;
12+
import java.nio.ByteBuffer;
13+
import java.nio.channels.AsynchronousByteChannel;
14+
import java.nio.channels.AsynchronousCloseException;
15+
import java.nio.channels.AsynchronousFileChannel;
16+
import java.nio.channels.Channels;
17+
import java.nio.channels.CompletionHandler;
18+
import java.nio.file.NoSuchFileException;
19+
import java.nio.file.Paths;
20+
import java.nio.file.StandardOpenOption;
21+
import java.util.concurrent.CompletableFuture;
22+
import java.util.concurrent.Future;
1423

1524
class NamedPipeSocket extends Socket {
1625

1726
private final String socketFileName;
1827

19-
private RandomAccessFile file;
20-
21-
private InputStream is;
22-
23-
private OutputStream os;
28+
private AsynchronousFileByteChannel channel;
2429

2530
NamedPipeSocket(String socketFileName) {
2631
this.socketFileName = socketFileName;
2732
}
2833

2934
@Override
3035
public void close() throws IOException {
31-
if (file != null) {
32-
file.close();
33-
file = null;
36+
if (channel != null) {
37+
channel.close();
3438
}
3539
}
3640

3741
@Override
38-
public void connect(SocketAddress endpoint) {
42+
public void connect(SocketAddress endpoint) throws IOException {
3943
connect(endpoint, 0);
4044
}
4145

4246
@Override
43-
public void connect(SocketAddress endpoint, int timeout) {
47+
public void connect(SocketAddress endpoint, int timeout) throws IOException {
4448
long startedAt = System.currentTimeMillis();
4549
timeout = Math.max(timeout, 10_000);
4650
while (true) {
4751
try {
48-
file = new RandomAccessFile(socketFileName, "rw");
52+
channel = new AsynchronousFileByteChannel(
53+
AsynchronousFileChannel.open(
54+
Paths.get(socketFileName),
55+
StandardOpenOption.READ,
56+
StandardOpenOption.WRITE
57+
)
58+
);
4959
break;
50-
} catch (FileNotFoundException e) {
60+
} catch (NoSuchFileException e) {
5161
if (System.currentTimeMillis() - startedAt >= timeout) {
5262
throw new RuntimeException(e);
5363
} else {
5464
Kernel32.INSTANCE.WaitNamedPipe(socketFileName, 100);
5565
}
5666
}
5767
}
58-
59-
is = new InputStream() {
60-
@Override
61-
public int read(byte[] bytes, int off, int len) throws IOException {
62-
return file.read(bytes, off, len);
63-
}
64-
65-
@Override
66-
public int read() throws IOException {
67-
return file.read();
68-
}
69-
70-
@Override
71-
public int read(byte[] bytes) throws IOException {
72-
return file.read(bytes);
73-
}
74-
};
75-
76-
os = new OutputStream() {
77-
@Override
78-
public void write(byte[] bytes, int off, int len) throws IOException {
79-
file.write(bytes, off, len);
80-
}
81-
82-
@Override
83-
public void write(int value) throws IOException {
84-
file.write(value);
85-
}
86-
87-
@Override
88-
public void write(byte[] bytes) throws IOException {
89-
file.write(bytes);
90-
}
91-
};
9268
}
9369

9470
@Override
9571
public InputStream getInputStream() {
96-
return is;
72+
return Channels.newInputStream(channel);
9773
}
9874

9975
@Override
10076
public OutputStream getOutputStream() {
101-
return os;
77+
return Channels.newOutputStream(channel);
10278
}
10379

10480
interface Kernel32 extends StdCallLibrary {
@@ -108,4 +84,75 @@ interface Kernel32 extends StdCallLibrary {
10884
@SuppressWarnings("checkstyle:methodname")
10985
boolean WaitNamedPipe(String lpNamedPipeName, int nTimeOut);
11086
}
87+
88+
private static class AsynchronousFileByteChannel implements AsynchronousByteChannel {
89+
private final AsynchronousFileChannel fileChannel;
90+
91+
AsynchronousFileByteChannel(AsynchronousFileChannel fileChannel) {
92+
this.fileChannel = fileChannel;
93+
}
94+
95+
@Override
96+
public <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler) {
97+
fileChannel.read(dst, 0, attachment, new CompletionHandler<Integer, A>() {
98+
@Override
99+
public void completed(Integer read, A attachment) {
100+
handler.completed(read > 0 ? read : -1, attachment);
101+
}
102+
103+
@Override
104+
public void failed(Throwable exc, A attachment) {
105+
if (exc instanceof AsynchronousCloseException) {
106+
handler.completed(-1, attachment);
107+
return;
108+
}
109+
handler.failed(exc, attachment);
110+
}
111+
});
112+
}
113+
114+
@Override
115+
public Future<Integer> read(ByteBuffer dst) {
116+
CompletableFutureHandler future = new CompletableFutureHandler();
117+
fileChannel.read(dst, 0, null, future);
118+
return future;
119+
}
120+
121+
@Override
122+
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
123+
fileChannel.write(src, 0, attachment, handler);
124+
}
125+
126+
@Override
127+
public Future<Integer> write(ByteBuffer src) {
128+
return fileChannel.write(src, 0);
129+
}
130+
131+
@Override
132+
public void close() throws IOException {
133+
fileChannel.close();
134+
}
135+
136+
@Override
137+
public boolean isOpen() {
138+
return fileChannel.isOpen();
139+
}
140+
141+
private static class CompletableFutureHandler extends CompletableFuture<Integer> implements CompletionHandler<Integer, Object> {
142+
143+
@Override
144+
public void completed(Integer read, Object attachment) {
145+
complete(read > 0 ? read : -1);
146+
}
147+
148+
@Override
149+
public void failed(Throwable exc, Object attachment) {
150+
if (exc instanceof AsynchronousCloseException) {
151+
complete(-1);
152+
return;
153+
}
154+
completeExceptionally(exc);
155+
}
156+
}
157+
}
111158
}
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
package com.github.dockerjava.okhttp;
2+
3+
import com.sun.jna.Native;
4+
import com.sun.jna.win32.StdCallLibrary;
5+
import com.sun.jna.win32.W32APIOptions;
6+
7+
import java.io.IOException;
8+
import java.io.InputStream;
9+
import java.io.OutputStream;
10+
import java.net.Socket;
11+
import java.net.SocketAddress;
12+
import java.nio.ByteBuffer;
13+
import java.nio.channels.AsynchronousByteChannel;
14+
import java.nio.channels.AsynchronousCloseException;
15+
import java.nio.channels.AsynchronousFileChannel;
16+
import java.nio.channels.Channels;
17+
import java.nio.channels.CompletionHandler;
18+
import java.nio.file.NoSuchFileException;
19+
import java.nio.file.Paths;
20+
import java.nio.file.StandardOpenOption;
21+
import java.util.concurrent.CompletableFuture;
22+
import java.util.concurrent.Future;
23+
24+
class NamedPipeSocket extends Socket {
25+
26+
private final String socketFileName;
27+
28+
private AsynchronousFileByteChannel channel;
29+
30+
NamedPipeSocket(String socketFileName) {
31+
this.socketFileName = socketFileName;
32+
}
33+
34+
@Override
35+
public void close() throws IOException {
36+
if (channel != null) {
37+
channel.close();
38+
}
39+
}
40+
41+
@Override
42+
public void connect(SocketAddress endpoint) throws IOException {
43+
connect(endpoint, 0);
44+
}
45+
46+
@Override
47+
public void connect(SocketAddress endpoint, int timeout) throws IOException {
48+
long startedAt = System.currentTimeMillis();
49+
50+
timeout = Math.max(timeout, 10_000);
51+
while (true) {
52+
try {
53+
channel = new AsynchronousFileByteChannel(
54+
AsynchronousFileChannel.open(
55+
Paths.get(socketFileName),
56+
StandardOpenOption.READ,
57+
StandardOpenOption.WRITE
58+
)
59+
);
60+
break;
61+
} catch (NoSuchFileException e) {
62+
if (System.currentTimeMillis() - startedAt >= timeout) {
63+
throw new RuntimeException(e);
64+
} else {
65+
Kernel32.INSTANCE.WaitNamedPipe(socketFileName, 100);
66+
}
67+
}
68+
}
69+
}
70+
71+
@Override
72+
public InputStream getInputStream() {
73+
return Channels.newInputStream(channel);
74+
}
75+
76+
@Override
77+
public OutputStream getOutputStream() {
78+
return Channels.newOutputStream(channel);
79+
}
80+
81+
interface Kernel32 extends StdCallLibrary {
82+
83+
Kernel32 INSTANCE = Native.load("kernel32", Kernel32.class, W32APIOptions.DEFAULT_OPTIONS);
84+
85+
@SuppressWarnings("checkstyle:methodname")
86+
boolean WaitNamedPipe(String lpNamedPipeName, int nTimeOut);
87+
}
88+
89+
private static class AsynchronousFileByteChannel implements AsynchronousByteChannel {
90+
private final AsynchronousFileChannel fileChannel;
91+
92+
AsynchronousFileByteChannel(AsynchronousFileChannel fileChannel) {
93+
this.fileChannel = fileChannel;
94+
}
95+
96+
@Override
97+
public <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler) {
98+
fileChannel.read(dst, 0, attachment, new CompletionHandler<Integer, A>() {
99+
@Override
100+
public void completed(Integer read, A attachment) {
101+
handler.completed(read > 0 ? read : -1, attachment);
102+
}
103+
104+
@Override
105+
public void failed(Throwable exc, A attachment) {
106+
if (exc instanceof AsynchronousCloseException) {
107+
handler.completed(-1, attachment);
108+
return;
109+
}
110+
handler.failed(exc, attachment);
111+
}
112+
});
113+
}
114+
115+
@Override
116+
public Future<Integer> read(ByteBuffer dst) {
117+
CompletableFutureHandler future = new CompletableFutureHandler();
118+
fileChannel.read(dst, 0, null, future);
119+
return future;
120+
}
121+
122+
@Override
123+
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
124+
fileChannel.write(src, 0, attachment, handler);
125+
}
126+
127+
@Override
128+
public Future<Integer> write(ByteBuffer src) {
129+
return fileChannel.write(src, 0);
130+
}
131+
132+
@Override
133+
public void close() throws IOException {
134+
fileChannel.close();
135+
}
136+
137+
@Override
138+
public boolean isOpen() {
139+
return fileChannel.isOpen();
140+
}
141+
142+
private static class CompletableFutureHandler extends CompletableFuture<Integer> implements CompletionHandler<Integer, Object> {
143+
144+
@Override
145+
public void completed(Integer read, Object attachment) {
146+
complete(read > 0 ? read : -1);
147+
}
148+
149+
@Override
150+
public void failed(Throwable exc, Object attachment) {
151+
if (exc instanceof AsynchronousCloseException) {
152+
complete(-1);
153+
return;
154+
}
155+
completeExceptionally(exc);
156+
}
157+
}
158+
}
159+
}

0 commit comments

Comments
 (0)