Skip to content

Commit 458310c

Browse files
committed
Add configurable base directory and improve chunked HTTP handling
1 parent eee7fd8 commit 458310c

File tree

8 files changed

+364
-40
lines changed

8 files changed

+364
-40
lines changed

src/main/java/io/github/intisy/docker/DockerProvider.java

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,83 @@
1010
* <p>
1111
* Each provider instance is isolated and can run simultaneously with other instances.
1212
* Use {@link #getInstanceId()} to get the unique identifier for this instance.
13+
* <p>
14+
* The base directory for storing Docker data can be configured using {@link #setBaseDirectory(Path)}
15+
* before creating any providers. By default, it uses {@code ~/.docker-java/}.
1316
*
1417
* @author Finn Birich
1518
*/
1619
public abstract class DockerProvider {
17-
protected static final Path DOCKER_DIR = Paths.get(System.getProperty("user.home"), ".docker-java");
20+
private static final Path DEFAULT_DOCKER_DIR = Paths.get(System.getProperty("user.home"), ".docker-java");
21+
private static final String DEFAULT_WSL_BASE = ".docker-java";
22+
23+
private static Path baseDirectory = DEFAULT_DOCKER_DIR;
24+
private static String wslBaseDirectory = DEFAULT_WSL_BASE;
25+
26+
/**
27+
* Set the base directory for storing Docker data and instances.
28+
* This must be called before creating any DockerProvider instances.
29+
* <p>
30+
* Example:
31+
* <pre>{@code
32+
* DockerProvider.setBaseDirectory(Paths.get("/custom/path/docker-java"));
33+
* DockerProvider provider = DockerProvider.get();
34+
* }</pre>
35+
*
36+
* @param path The base directory path
37+
*/
38+
public static void setBaseDirectory(Path path) {
39+
baseDirectory = path;
40+
}
41+
42+
/**
43+
* Get the current base directory for storing Docker data.
44+
*
45+
* @return The base directory path
46+
*/
47+
public static Path getBaseDirectory() {
48+
return baseDirectory;
49+
}
50+
51+
/**
52+
* Set the base directory path for WSL2 (Windows Subsystem for Linux).
53+
* This is used when running Docker in WSL2 mode on Windows.
54+
* The path is relative to the WSL user's home directory.
55+
* <p>
56+
* Example:
57+
* <pre>{@code
58+
* DockerProvider.setWslBaseDirectory(".my-docker-data");
59+
* // Results in ~/. my-docker-data/ inside WSL2
60+
* }</pre>
61+
*
62+
* @param path The WSL base directory path (relative to home)
63+
*/
64+
public static void setWslBaseDirectory(String path) {
65+
wslBaseDirectory = path;
66+
}
67+
68+
/**
69+
* Get the WSL base directory path.
70+
*
71+
* @return The WSL base directory path (relative to home)
72+
*/
73+
public static String getWslBaseDirectory() {
74+
return wslBaseDirectory;
75+
}
76+
77+
/**
78+
* Reset the base directory to the default ({@code ~/.docker-java/}).
79+
*/
80+
public static void resetBaseDirectory() {
81+
baseDirectory = DEFAULT_DOCKER_DIR;
82+
wslBaseDirectory = DEFAULT_WSL_BASE;
83+
}
84+
85+
/**
86+
* @deprecated Use {@link #getBaseDirectory()} instead
87+
*/
88+
@Deprecated
89+
protected static final Path DOCKER_DIR = DEFAULT_DOCKER_DIR;
1890

1991
/**
2092
* Get the appropriate DockerProvider for the current operating system.

src/main/java/io/github/intisy/docker/LinuxDockerProvider.java

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,9 @@ public class LinuxDockerProvider extends DockerProvider {
5555

5656
public LinuxDockerProvider() {
5757
this.instanceId = UUID.randomUUID().toString().substring(0, 8);
58-
this.instanceDir = DOCKER_DIR.resolve("instances").resolve(instanceId);
59-
setPath(DOCKER_DIR);
58+
Path baseDir = getBaseDirectory();
59+
this.instanceDir = baseDir.resolve("instances").resolve(instanceId);
60+
setPath(baseDir);
6061
log.debug("Created LinuxDockerProvider with instance ID: {}", instanceId);
6162
}
6263

@@ -253,16 +254,37 @@ public void stop() {
253254
}
254255

255256
private void cleanupInstanceDirectory() {
257+
if (!Files.exists(instanceDir)) {
258+
return;
259+
}
260+
256261
try {
257-
if (Files.exists(instanceDir)) {
258-
try (java.util.stream.Stream<Path> walk = Files.walk(instanceDir)) {
259-
walk.sorted(java.util.Comparator.reverseOrder())
260-
.map(Path::toFile)
261-
.forEach(File::delete);
262-
}
262+
ProcessBuilder pb = new ProcessBuilder("rm", "-rf", instanceDir.toString());
263+
pb.redirectErrorStream(true);
264+
Process process = pb.start();
265+
boolean completed = process.waitFor(30, TimeUnit.SECONDS);
266+
if (!completed) {
267+
process.destroyForcibly();
268+
log.warn("Cleanup timed out for instance directory: {}", instanceDir);
269+
} else if (process.exitValue() != 0) {
270+
log.warn("Cleanup returned non-zero exit code for instance directory: {}", instanceDir);
263271
}
264-
} catch (IOException e) {
272+
} catch (IOException | InterruptedException e) {
265273
log.warn("Failed to clean up instance directory: {}", e.getMessage());
274+
try {
275+
if (Files.exists(instanceDir)) {
276+
try (java.util.stream.Stream<Path> walk = Files.walk(instanceDir)) {
277+
walk.sorted(java.util.Comparator.reverseOrder())
278+
.forEach(path -> {
279+
try {
280+
Files.deleteIfExists(path);
281+
} catch (IOException ignored) {
282+
}
283+
});
284+
}
285+
}
286+
} catch (IOException ignored) {
287+
}
266288
}
267289
}
268290

src/main/java/io/github/intisy/docker/MacDockerProvider.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ public class MacDockerProvider extends DockerProvider {
4545

4646
public MacDockerProvider() {
4747
this.instanceId = UUID.randomUUID().toString().substring(0, 8);
48-
this.instanceDir = DOCKER_DIR.resolve("instances").resolve(instanceId);
48+
Path baseDir = getBaseDirectory();
49+
this.instanceDir = baseDir.resolve("instances").resolve(instanceId);
4950
this.runDir = instanceDir.resolve("run");
5051
this.dataDir = instanceDir.resolve("data");
5152
this.execDir = instanceDir.resolve("exec");

src/main/java/io/github/intisy/docker/WindowsDockerProvider.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ public class WindowsDockerProvider extends DockerProvider {
4646

4747
public WindowsDockerProvider() {
4848
this.instanceId = UUID.randomUUID().toString().substring(0, 8);
49-
this.instanceDir = DOCKER_DIR.resolve("instances").resolve(instanceId);
49+
Path baseDir = getBaseDirectory();
50+
this.instanceDir = baseDir.resolve("instances").resolve(instanceId);
5051
this.pipeName = "docker_java_" + instanceId;
5152
this.dockerPipePath = Paths.get("\\\\.\\pipe\\" + pipeName);
5253
this.dataDir = instanceDir.resolve("data");
@@ -255,9 +256,11 @@ private void startWsl2Docker() throws IOException, InterruptedException {
255256
}
256257
log.debug("WSL home directory: {}", wslHome);
257258

258-
String wslDataDir = wslHome + "/.docker-java/instances/" + instanceId + "/data";
259-
String wslExecDir = wslHome + "/.docker-java/instances/" + instanceId + "/exec";
260-
String wslPidFile = wslHome + "/.docker-java/instances/" + instanceId + "/docker.pid";
259+
String wslBase = getWslBaseDirectory();
260+
String wslBaseDir = wslHome + "/" + wslBase + "/instances/" + instanceId;
261+
String wslDataDir = wslBaseDir + "/data";
262+
String wslExecDir = wslBaseDir + "/exec";
263+
String wslPidFile = wslBaseDir + "/docker.pid";
261264

262265
log.debug("Creating directories in WSL2 (distro: {})...", wslDistro);
263266
String mkdirResult = runWslCommand("mkdir -p " + wslDataDir + " " + wslExecDir + " && echo ok", false, 10);
@@ -574,9 +577,10 @@ public void stop() {
574577
if (dockerProcess != null) {
575578
if (usingWsl2 && wslDistro != null) {
576579
try {
580+
String wslBase = getWslBaseDirectory();
577581
ProcessBuilder pb = new ProcessBuilder("wsl", "-d", wslDistro, "-e", "bash", "-c",
578582
"sudo -n pkill -f 'dockerd.*" + dockerPort + "' 2>/dev/null ; " +
579-
"rm -rf ~/.docker-java/instances/" + instanceId);
583+
"rm -rf ~/" + wslBase + "/instances/" + instanceId);
580584
pb.start().waitFor(5, TimeUnit.SECONDS);
581585
} catch (IOException | InterruptedException e) {
582586
log.warn("Failed to stop WSL2 Docker daemon: {}", e.getMessage());

src/main/java/io/github/intisy/docker/command/system/InfoCmd.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,18 @@ public SystemInfo exec() {
3434
}
3535

3636
String body = response.getBody();
37-
log.debug("Raw /info response (first 2000 chars): {}",
38-
body.length() > 2000 ? body.substring(0, 2000) + "..." : body);
3937

4038
try {
4139
return client.getGson().fromJson(body, SystemInfo.class);
4240
} catch (Exception e) {
43-
log.error("Failed to parse /info response. Response body: {}", body);
41+
System.err.println("=== DOCKER INFO PARSE ERROR ===");
42+
System.err.println("Exception: " + e.getClass().getName() + ": " + e.getMessage());
43+
if (e.getCause() != null) {
44+
System.err.println("Caused by: " + e.getCause().getClass().getName() + ": " + e.getCause().getMessage());
45+
}
46+
System.err.println("Response body (first 3000 chars):");
47+
System.err.println(body.length() > 3000 ? body.substring(0, 3000) + "..." : body);
48+
System.err.println("=== END DOCKER INFO PARSE ERROR ===");
4449
throw e;
4550
}
4651
} catch (IOException e) {

0 commit comments

Comments
 (0)