From 0669456a4fb67b786fb68e4d835d681b1cc9c150 Mon Sep 17 00:00:00 2001 From: pchambre Date: Mon, 29 Dec 2025 18:02:24 +0100 Subject: [PATCH 1/4] Added handling to convert between slashes needed by the Web UI and backslashes on the local filesystem. Also removed spaces from default paths, since they are tedious on Windows. To build on Windows a version after 1.2.0 of the GWT Gradle plug-in is needed. Those changes are not included in this PR; so this branch would still need to be built on Linux, or Mac, or maybe under WSL. --- .../io/deephaven/configuration/DataDir.java | 1 + .../FilesystemStorageServiceGrpcImpl.java | 32 ++++++++++++++++--- .../deephaven/server/runner/MainHelper.java | 18 ++++++++--- 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/Configuration/src/main/java/io/deephaven/configuration/DataDir.java b/Configuration/src/main/java/io/deephaven/configuration/DataDir.java index 9dfedce05cd..308501a763d 100644 --- a/Configuration/src/main/java/io/deephaven/configuration/DataDir.java +++ b/Configuration/src/main/java/io/deephaven/configuration/DataDir.java @@ -12,6 +12,7 @@ public class DataDir { @Deprecated private static final String WORKSPACE_PROPERTY = "workspace"; public static final String ENV_VAR = "DEEPHAVEN_DATA_DIR"; + public static final String WINDOWS_SEPARATOR = "\\"; private static final String DEFAULT_DATA_DIR = "."; diff --git a/server/src/main/java/io/deephaven/server/notebook/FilesystemStorageServiceGrpcImpl.java b/server/src/main/java/io/deephaven/server/notebook/FilesystemStorageServiceGrpcImpl.java index ee60bdfd0e6..dcad754bddb 100644 --- a/server/src/main/java/io/deephaven/server/notebook/FilesystemStorageServiceGrpcImpl.java +++ b/server/src/main/java/io/deephaven/server/notebook/FilesystemStorageServiceGrpcImpl.java @@ -10,7 +10,6 @@ import com.google.rpc.Code; import io.deephaven.configuration.Configuration; import io.deephaven.configuration.DataDir; -import io.deephaven.extensions.barrage.util.GrpcUtil; import io.deephaven.internal.log.LoggerFactory; import io.deephaven.io.logger.Logger; import io.deephaven.proto.backplane.grpc.CreateDirectoryRequest; @@ -114,14 +113,22 @@ public FilesystemStorageServiceGrpcImpl( } private Path resolveOrThrow(String incomingPath) { - if (incomingPath.startsWith(File.separator)) { + String resolveDetails = "\nIncoming path is: " + incomingPath; + resolveDetails = resolveDetails + "\nFile separator is: " + File.separator; + incomingPath = maybeReplaceFileSeparators(incomingPath); + while (incomingPath.length() > 0 && + (incomingPath.startsWith(REQUIRED_PATH_PREFIX))) { incomingPath = incomingPath.substring(1); } Path resolved = root.resolve(incomingPath).normalize(); + resolveDetails = resolveDetails + "\nResolved path is: " + resolved; + resolveDetails = resolveDetails + "\nWhich we want to start with root: " + root; + if (resolved.startsWith(root)) { return resolved; } - throw Exceptions.statusRuntimeException(Code.INVALID_ARGUMENT, "Invalid path: " + incomingPath); + throw Exceptions.statusRuntimeException(Code.INVALID_ARGUMENT, "Invalid path: " + incomingPath + + "." + resolveDetails); } private void requireNotRoot(Path path, String message) { @@ -130,6 +137,20 @@ private void requireNotRoot(Path path, String message) { } } + /** + * On Windows systems, we need to replace backslashes with forward slashes for the Web UI + * @param path Path String that may need to be modified + * @return Original path on non-Windows systems, or path with backslashes replaced with + * slashes on Windows systems. + */ + private String maybeReplaceFileSeparators(final String path) { + if (File.separator.equals(DataDir.WINDOWS_SEPARATOR)) { + return path.replace(DataDir.WINDOWS_SEPARATOR, REQUIRED_PATH_PREFIX); + } else { + return path; + } + } + @Override public void listItems( @NotNull final ListItemsRequest request, @@ -148,8 +169,9 @@ public void listItems( } BasicFileAttributes attrs = Files.readAttributes(p, BasicFileAttributes.class); boolean isDirectory = attrs.isDirectory(); + String relativePath = maybeReplaceFileSeparators(root.relativize(p).toString()); ItemInfo.Builder info = ItemInfo.newBuilder() - .setPath(REQUIRED_PATH_PREFIX + root.relativize(p)); + .setPath(REQUIRED_PATH_PREFIX + relativePath); if (isDirectory) { info.setType(ItemType.DIRECTORY); } else { @@ -172,7 +194,7 @@ private static PathMatcher createPathFilter(String filterGlob) { if (filterGlob.contains("**")) { throw Exceptions.statusRuntimeException(Code.INVALID_ARGUMENT, "Bad glob, only single `*`s are supported"); } - if (filterGlob.contains(File.separator)) { + if (filterGlob.contains(File.separator) || filterGlob.contains(REQUIRED_PATH_PREFIX)) { throw Exceptions.statusRuntimeException(Code.INVALID_ARGUMENT, "Bad glob, only the same directory can be checked"); } diff --git a/server/src/main/java/io/deephaven/server/runner/MainHelper.java b/server/src/main/java/io/deephaven/server/runner/MainHelper.java index 9146b3782e2..1e9e7652f91 100644 --- a/server/src/main/java/io/deephaven/server/runner/MainHelper.java +++ b/server/src/main/java/io/deephaven/server/runner/MainHelper.java @@ -25,7 +25,7 @@ import io.deephaven.util.process.ProcessEnvironment; import org.jetbrains.annotations.NotNull; import org.slf4j.bridge.SLF4JBridgeHandler; - +import java.io.File; import java.io.IOException; import java.io.Reader; import java.nio.charset.Charset; @@ -63,6 +63,16 @@ private static void bootstrapFromFile(Path configFile) throws IOException { } } + // Spaces in the paths are tedious on Windows + private static String maybeRemoveSpaces(final String pathToCheck) { + if (pathToCheck == null) { + return null; + } + return (File.separator.equals(DataDir.WINDOWS_SEPARATOR)) ? + pathToCheck.replace(" ","") : + pathToCheck; + } + @VisibleForTesting public static void bootstrapProjectDirectories() throws IOException { final String applicationName = @@ -72,9 +82,9 @@ public static void bootstrapProjectDirectories() throws IOException { final dev.dirs.ProjectDirectories defaultDirectories = dev.dirs.ProjectDirectories.from("io", "Deephaven Data Labs", applicationName); - final Path cacheDir = CacheDir.getOrSet(defaultDirectories.cacheDir); - final Path configDir = ConfigDir.getOrSet(defaultDirectories.configDir); - final Path dataDir = DataDir.getOrSet(defaultDirectories.dataDir); + final Path cacheDir = CacheDir.getOrSet(maybeRemoveSpaces(defaultDirectories.cacheDir)); + final Path configDir = ConfigDir.getOrSet(maybeRemoveSpaces(defaultDirectories.configDir)); + final Path dataDir = DataDir.getOrSet(maybeRemoveSpaces(defaultDirectories.dataDir)); Files.createDirectories(cacheDir); Files.createDirectories(dataDir); From e020a3c3516ab1fe49ff33988caa1d110afb7fb1 Mon Sep 17 00:00:00 2001 From: pchambre Date: Mon, 29 Dec 2025 18:58:34 +0100 Subject: [PATCH 2/4] Ran spotlessApply --- .../server/notebook/FilesystemStorageServiceGrpcImpl.java | 4 ++-- .../src/main/java/io/deephaven/server/runner/MainHelper.java | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/server/src/main/java/io/deephaven/server/notebook/FilesystemStorageServiceGrpcImpl.java b/server/src/main/java/io/deephaven/server/notebook/FilesystemStorageServiceGrpcImpl.java index dcad754bddb..ac422adc38a 100644 --- a/server/src/main/java/io/deephaven/server/notebook/FilesystemStorageServiceGrpcImpl.java +++ b/server/src/main/java/io/deephaven/server/notebook/FilesystemStorageServiceGrpcImpl.java @@ -139,9 +139,9 @@ private void requireNotRoot(Path path, String message) { /** * On Windows systems, we need to replace backslashes with forward slashes for the Web UI + * * @param path Path String that may need to be modified - * @return Original path on non-Windows systems, or path with backslashes replaced with - * slashes on Windows systems. + * @return Original path on non-Windows systems, or path with backslashes replaced with slashes on Windows systems. */ private String maybeReplaceFileSeparators(final String path) { if (File.separator.equals(DataDir.WINDOWS_SEPARATOR)) { diff --git a/server/src/main/java/io/deephaven/server/runner/MainHelper.java b/server/src/main/java/io/deephaven/server/runner/MainHelper.java index 1e9e7652f91..5bd2eb5cb5d 100644 --- a/server/src/main/java/io/deephaven/server/runner/MainHelper.java +++ b/server/src/main/java/io/deephaven/server/runner/MainHelper.java @@ -68,9 +68,7 @@ private static String maybeRemoveSpaces(final String pathToCheck) { if (pathToCheck == null) { return null; } - return (File.separator.equals(DataDir.WINDOWS_SEPARATOR)) ? - pathToCheck.replace(" ","") : - pathToCheck; + return (File.separator.equals(DataDir.WINDOWS_SEPARATOR)) ? pathToCheck.replace(" ", "") : pathToCheck; } @VisibleForTesting From 52ff2ec32f03723ac76d0a2bee7e7c8139fb6dbe Mon Sep 17 00:00:00 2001 From: pchambre Date: Tue, 6 Jan 2026 20:56:29 +0100 Subject: [PATCH 3/4] Some changes from code review. Added a separate LINUX_SEPARATOR constant to use in place of REQUIRED_PATH_PREFIX in new code. --- .../FilesystemStorageServiceGrpcImpl.java | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) mode change 100644 => 100755 server/src/main/java/io/deephaven/server/notebook/FilesystemStorageServiceGrpcImpl.java diff --git a/server/src/main/java/io/deephaven/server/notebook/FilesystemStorageServiceGrpcImpl.java b/server/src/main/java/io/deephaven/server/notebook/FilesystemStorageServiceGrpcImpl.java old mode 100644 new mode 100755 index ac422adc38a..7aecb2c2435 --- a/server/src/main/java/io/deephaven/server/notebook/FilesystemStorageServiceGrpcImpl.java +++ b/server/src/main/java/io/deephaven/server/notebook/FilesystemStorageServiceGrpcImpl.java @@ -91,6 +91,12 @@ public class FilesystemStorageServiceGrpcImpl extends StorageServiceGrpc.Storage */ @Deprecated private static final String REQUIRED_PATH_PREFIX = "/"; + /** + * Although REQUIRED_PATH_PREFIX has been deprecated, it is still used; this constant is for the + * same forward slash, to detect situations where the UI path separator is different from the + * OS path separator (i.e. on Windows). + */ + private static final String LINUX_SEPARATOR = "/"; private final Path root = Paths.get(STORAGE_PATH).normalize(); private final SessionService sessionService; @@ -113,16 +119,13 @@ public FilesystemStorageServiceGrpcImpl( } private Path resolveOrThrow(String incomingPath) { - String resolveDetails = "\nIncoming path is: " + incomingPath; + String resolveDetails = "\nOriginal incoming path is: " + incomingPath; resolveDetails = resolveDetails + "\nFile separator is: " + File.separator; incomingPath = maybeReplaceFileSeparators(incomingPath); - while (incomingPath.length() > 0 && - (incomingPath.startsWith(REQUIRED_PATH_PREFIX))) { + while (incomingPath.startsWith(REQUIRED_PATH_PREFIX)) { incomingPath = incomingPath.substring(1); } Path resolved = root.resolve(incomingPath).normalize(); - resolveDetails = resolveDetails + "\nResolved path is: " + resolved; - resolveDetails = resolveDetails + "\nWhich we want to start with root: " + root; if (resolved.startsWith(root)) { return resolved; @@ -139,13 +142,13 @@ private void requireNotRoot(Path path, String message) { /** * On Windows systems, we need to replace backslashes with forward slashes for the Web UI - * * @param path Path String that may need to be modified - * @return Original path on non-Windows systems, or path with backslashes replaced with slashes on Windows systems. + * @return Original path on non-Windows systems, or path with backslashes replaced with + * slashes on Windows systems. */ private String maybeReplaceFileSeparators(final String path) { if (File.separator.equals(DataDir.WINDOWS_SEPARATOR)) { - return path.replace(DataDir.WINDOWS_SEPARATOR, REQUIRED_PATH_PREFIX); + return path.replace(DataDir.WINDOWS_SEPARATOR, LINUX_SEPARATOR); } else { return path; } @@ -194,7 +197,7 @@ private static PathMatcher createPathFilter(String filterGlob) { if (filterGlob.contains("**")) { throw Exceptions.statusRuntimeException(Code.INVALID_ARGUMENT, "Bad glob, only single `*`s are supported"); } - if (filterGlob.contains(File.separator) || filterGlob.contains(REQUIRED_PATH_PREFIX)) { + if (filterGlob.contains(File.separator) || filterGlob.contains(LINUX_SEPARATOR)) { throw Exceptions.statusRuntimeException(Code.INVALID_ARGUMENT, "Bad glob, only the same directory can be checked"); } From 747a65cb585eda62311be90a753c2bfab06a6a49 Mon Sep 17 00:00:00 2001 From: pchambre Date: Tue, 6 Jan 2026 21:06:07 +0100 Subject: [PATCH 4/4] Ran spotlessApply. --- .../notebook/FilesystemStorageServiceGrpcImpl.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/server/src/main/java/io/deephaven/server/notebook/FilesystemStorageServiceGrpcImpl.java b/server/src/main/java/io/deephaven/server/notebook/FilesystemStorageServiceGrpcImpl.java index 7aecb2c2435..b2603e5c852 100755 --- a/server/src/main/java/io/deephaven/server/notebook/FilesystemStorageServiceGrpcImpl.java +++ b/server/src/main/java/io/deephaven/server/notebook/FilesystemStorageServiceGrpcImpl.java @@ -92,9 +92,8 @@ public class FilesystemStorageServiceGrpcImpl extends StorageServiceGrpc.Storage @Deprecated private static final String REQUIRED_PATH_PREFIX = "/"; /** - * Although REQUIRED_PATH_PREFIX has been deprecated, it is still used; this constant is for the - * same forward slash, to detect situations where the UI path separator is different from the - * OS path separator (i.e. on Windows). + * Although REQUIRED_PATH_PREFIX has been deprecated, it is still used; this constant is for the same forward slash, + * to detect situations where the UI path separator is different from the OS path separator (i.e. on Windows). */ private static final String LINUX_SEPARATOR = "/"; @@ -142,9 +141,9 @@ private void requireNotRoot(Path path, String message) { /** * On Windows systems, we need to replace backslashes with forward slashes for the Web UI + * * @param path Path String that may need to be modified - * @return Original path on non-Windows systems, or path with backslashes replaced with - * slashes on Windows systems. + * @return Original path on non-Windows systems, or path with backslashes replaced with slashes on Windows systems. */ private String maybeReplaceFileSeparators(final String path) { if (File.separator.equals(DataDir.WINDOWS_SEPARATOR)) {