Skip to content

Commit 67bad74

Browse files
authored
Migrate to JNA for Windows Named Pipes (#339)
1 parent 6e17db2 commit 67bad74

File tree

12 files changed

+658
-273
lines changed

12 files changed

+658
-273
lines changed

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ dependencies {
7171
isTransitive = true
7272
}
7373
implementation(libs.junixsocketCommon)
74+
implementation(libs.bundles.jna)
7475

7576
testImplementation("org.junit.jupiter:junit-jupiter-api:5.13.4")
7677
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.13.4")

gradle/libs.versions.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
[versions]
2+
jna = "5.17.0"
23
junixsocket = "2.10.1"
34
junixsocketVersionrange = "[2.4,3)"
45
kotlin = "2.2.0"
@@ -13,7 +14,8 @@ slf4j = "2.0.17"
1314
slf4jVersionrange = "[1.7,3)"
1415

1516
[libraries]
16-
junit4 = { module = "junit:junit", version = "4.13.2" }
17+
jna = { module = "net.java.dev.jna:jna", version.ref = "jna" }
18+
jnaPlatform = { module = "net.java.dev.jna:jna-platform", version.ref = "jna" }
1719
junixsocketCore = { module = "com.kohlschutter.junixsocket:junixsocket-core", version.ref = "junixsocket" }
1820
junixsocketCommon = { module = "com.kohlschutter.junixsocket:junixsocket-common", version.ref = "junixsocket" }
1921
kotlin = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
@@ -28,5 +30,6 @@ slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
2830

2931
[bundles]
3032
kotlin = ["kotlin", "kotlinCommon", "kotlinJdk7", "kotlinJdk8"]
33+
jna = ["jna", "jnaPlatform"]
3134
junixsocket = ["junixsocketCore", "junixsocketCommon"]
3235
okio = ["okio", "okioJvm"]

src/main/java/de/gesellix/docker/client/filesocket/AsynchronousFileByteChannel.java

Lines changed: 0 additions & 83 deletions
This file was deleted.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package de.gesellix.docker.client.filesocket;
2+
3+
import com.sun.jna.Library;
4+
import com.sun.jna.Native;
5+
import com.sun.jna.Pointer;
6+
import com.sun.jna.platform.win32.WinBase;
7+
import com.sun.jna.platform.win32.WinDef;
8+
import com.sun.jna.platform.win32.WinNT;
9+
import com.sun.jna.ptr.IntByReference;
10+
import com.sun.jna.win32.W32APIOptions;
11+
12+
public interface ExtendedKernel32 extends Library {
13+
14+
ExtendedKernel32 INSTANCE = Native.load("kernel32", ExtendedKernel32.class, W32APIOptions.DEFAULT_OPTIONS);
15+
16+
boolean GetOverlappedResult(
17+
WinNT.HANDLE hFile,
18+
WinBase.OVERLAPPED lpOverlapped,
19+
IntByReference lpNumberOfBytesTransferred,
20+
boolean bWait
21+
);
22+
23+
/**
24+
* Cancels all pending I/O operations on the specified file handle.
25+
*
26+
* @param hFile The handle to the file or I/O device.
27+
* @param lpOverlapped Reserved; should be NULL.
28+
* @return true if successful; false otherwise.
29+
*/
30+
boolean CancelIoEx(
31+
WinNT.HANDLE hFile,
32+
Pointer lpOverlapped
33+
);
34+
35+
WinDef.DWORD WaitForSingleObject(
36+
WinNT.HANDLE hHandle,
37+
int dwMilliseconds
38+
);
39+
40+
boolean CloseHandle(
41+
WinNT.HANDLE hObject
42+
);
43+
}

src/main/java/de/gesellix/docker/client/filesocket/NamedPipeDelegatingInputStream.java

Lines changed: 0 additions & 35 deletions
This file was deleted.

src/main/java/de/gesellix/docker/client/filesocket/NamedPipeDelegatingOutputStream.java

Lines changed: 0 additions & 35 deletions
This file was deleted.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package de.gesellix.docker.client.filesocket;
2+
3+
import java.io.IOException;
4+
5+
import org.jetbrains.annotations.NotNull;
6+
7+
import com.sun.jna.platform.win32.Kernel32;
8+
import com.sun.jna.platform.win32.WinError;
9+
import com.sun.jna.platform.win32.WinNT;
10+
import com.sun.jna.ptr.IntByReference;
11+
12+
import okio.Buffer;
13+
import okio.Sink;
14+
import okio.Timeout;
15+
16+
public class NamedPipeSink implements Sink {
17+
18+
private final WinNT.HANDLE handle;
19+
private final int timeoutMillis;
20+
21+
public NamedPipeSink(WinNT.HANDLE handle, int timeoutMillis) {
22+
this.handle = handle;
23+
this.timeoutMillis = timeoutMillis;
24+
}
25+
26+
@Override
27+
public void write(@NotNull Buffer source, long byteCount) throws IOException {
28+
if (byteCount == 0) {
29+
return;
30+
}
31+
if (byteCount > Integer.MAX_VALUE) {
32+
throw new IllegalArgumentException("Can only write max " + Integer.MAX_VALUE + " bytes");
33+
}
34+
35+
byte[] data = source.readByteArray(byteCount);
36+
IntByReference bytesWritten = new IntByReference();
37+
//boolean ok = NamedPipeUtils.writeFromBuffer(handle, data, data.length, bytesWritten);
38+
boolean ok = NamedPipeUtils.writeOverlapped(handle, data, data.length, bytesWritten, timeoutMillis);
39+
if (!ok) {
40+
int err = Kernel32.INSTANCE.GetLastError();
41+
if (err == WinError.ERROR_OPERATION_ABORTED) {
42+
// Expected when CancelIoEx() is called during close()
43+
return;
44+
}
45+
throw new IOException("Failed to write to Named Pipe. WinError=" + err);
46+
}
47+
48+
if (bytesWritten.getValue() <= 0) {
49+
throw new IOException("No bytes written to Named Pipe");
50+
}
51+
}
52+
53+
@Override
54+
public void flush() {
55+
// No-op for Named Pipes
56+
}
57+
58+
@NotNull
59+
@Override
60+
public Timeout timeout() {
61+
return Timeout.NONE;
62+
}
63+
64+
@Override
65+
public void close() {
66+
// No-op, handle closed by NamedPipeSocket
67+
}
68+
}

0 commit comments

Comments
 (0)