Skip to content

Commit 60da219

Browse files
committed
Use SPI to inject MemoryFileSystem URL protocol support
1 parent 52392d7 commit 60da219

File tree

5 files changed

+109
-2
lines changed

5 files changed

+109
-2
lines changed

java-compiler-testing/src/main/java/io/github/ascopes/jct/workspaces/impl/MemoryFileSystemProvider.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@
1616
package io.github.ascopes.jct.workspaces.impl;
1717

1818
import com.github.marschall.memoryfilesystem.MemoryFileSystemBuilder;
19+
import com.github.marschall.memoryfilesystem.memory.MemoryURLStreamHandlerFactory;
1920
import io.github.ascopes.jct.workspaces.RamFileSystemProvider;
2021
import java.io.IOException;
2122
import java.io.UncheckedIOException;
23+
import java.net.URLStreamHandler;
24+
import java.net.spi.URLStreamHandlerProvider;
2225
import java.nio.file.FileSystem;
2326
import org.apiguardian.api.API;
2427
import org.apiguardian.api.API.Status;
@@ -63,4 +66,36 @@ public FileSystem createFileSystem(String name) {
6366
throw new UncheckedIOException("could not create file system with name: " + name, e);
6467
}
6568
}
69+
70+
/**
71+
* Java 9 SPI-based provider handler to register the URL protocol handler for the MemoryFileSystem
72+
* API implicitly at runtime. This enables URL resolution to work correctly, which is needed to
73+
* support {@link java.net.URLClassLoader} elsewhere.
74+
*
75+
* <p>Internally, this just adapts a {@link MemoryURLStreamHandlerFactory} into a
76+
* {@link URLStreamHandlerProvider} so that the JPMS SPI mechanism can load this provider eagerly
77+
* as soon as possible without needing to use JVM flags or call explicit methods during
78+
* classloading.
79+
*
80+
* @author Ashley Scopes
81+
* @since 0.7.0
82+
*/
83+
@API(since = "0.7.0", status = Status.INTERNAL)
84+
public static final class MemoryFileSystemUrlHandlerProvider extends URLStreamHandlerProvider {
85+
86+
private final MemoryURLStreamHandlerFactory factory;
87+
88+
public MemoryFileSystemUrlHandlerProvider() {
89+
factory = new MemoryURLStreamHandlerFactory();
90+
}
91+
92+
@Override
93+
public URLStreamHandler createURLStreamHandler(String protocol) {
94+
// This check can be removed once https://github.com/marschall/memoryfilesystem/pull/144 is
95+
// addressed.
96+
return com.github.marschall.memoryfilesystem.MemoryFileSystemProvider.SCHEME.equals(protocol)
97+
? factory.createURLStreamHandler(protocol)
98+
: null;
99+
}
100+
}
66101
}

java-compiler-testing/src/main/java/module-info.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
*/
1616
import io.github.ascopes.jct.junit.JctExtension;
1717
import io.github.ascopes.jct.workspaces.RamFileSystemProvider;
18+
import io.github.ascopes.jct.workspaces.impl.MemoryFileSystemProvider.MemoryFileSystemUrlHandlerProvider;
1819
import org.junit.jupiter.api.extension.Extension;
20+
import java.net.spi.URLStreamHandlerProvider;
1921

2022
/**
2123
* A framework for performing exhaustive integration testing against Java compilers in modern Java
@@ -133,7 +135,7 @@
133135
/// SERVICE PROVIDER INTERFACES ///
134136
///////////////////////////////////
135137

136-
provides Extension with JctExtension;
138+
provides URLStreamHandlerProvider with MemoryFileSystemUrlHandlerProvider;
137139
uses RamFileSystemProvider;
138140

139141
//////////////////////////////////////////////////////

java-compiler-testing/src/test/java/io/github/ascopes/jct/tests/unit/workspaces/impl/MemoryFileSystemProviderImplTest.java

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,28 @@
1818
import static io.github.ascopes.jct.tests.helpers.Fixtures.someText;
1919
import static org.assertj.core.api.Assertions.assertThat;
2020

21+
import com.github.marschall.memoryfilesystem.memory.Handler;
2122
import io.github.ascopes.jct.workspaces.impl.MemoryFileSystemProvider;
2223
import java.io.ByteArrayOutputStream;
2324
import java.io.IOException;
25+
import java.net.MalformedURLException;
26+
import java.net.URL;
27+
import java.net.spi.URLStreamHandlerProvider;
2428
import java.nio.ByteBuffer;
2529
import java.nio.charset.StandardCharsets;
2630
import java.nio.file.Files;
2731
import java.nio.file.Path;
2832
import java.nio.file.StandardOpenOption;
2933
import java.util.ArrayList;
34+
import java.util.ServiceLoader;
3035
import java.util.stream.Collectors;
3136
import java.util.stream.Stream;
37+
import io.github.ascopes.jct.workspaces.impl.MemoryFileSystemProvider.MemoryFileSystemUrlHandlerProvider;
3238
import org.junit.jupiter.api.DisplayName;
39+
import org.junit.jupiter.api.Nested;
3340
import org.junit.jupiter.api.Test;
41+
import org.junit.jupiter.params.ParameterizedTest;
42+
import org.junit.jupiter.params.provider.ValueSource;
3443

3544

3645
/**
@@ -194,4 +203,63 @@ void theCreatedFileSystemSupportsUrls() throws IOException {
194203
}
195204
}
196205
}
206+
207+
@DisplayName("MemoryFileSystemUrlHandlerProvider tests")
208+
@Nested
209+
class MemoryFileSystemUrlHandlerProviderTest {
210+
211+
@DisplayName("The provider is registered as SPI in the module descriptor")
212+
@Test
213+
void providerIsRegisteredAsSpiInModuleDescriptor() {
214+
// Given
215+
var module = MemoryFileSystemUrlHandlerProvider.class.getModule();
216+
var providers = module.getDescriptor()
217+
.provides()
218+
.stream()
219+
.filter(provide -> provide.service().equals(URLStreamHandlerProvider.class.getName()))
220+
.flatMap(provide -> provide.providers().stream());
221+
222+
// Then
223+
assertThat(providers)
224+
.contains(MemoryFileSystemUrlHandlerProvider.class.getName());
225+
}
226+
227+
@DisplayName("The provider returns a handler for the 'memory' protocol")
228+
@ValueSource(strings = {
229+
"memory://foo/bar/baz",
230+
"memory:da32e382-ece7-11ed-a05b-0242ac120003://foo/bar/baz",
231+
"memory:cc26d303-e997-4ea2-be2b-6b4b6c042b11://foo/bar/baz",
232+
})
233+
@ParameterizedTest(name = "for URL {0}")
234+
void providerReturnsHandlerForMemoryProtocol(String urlString) throws MalformedURLException {
235+
// Given
236+
var url = new URL(urlString);
237+
var provider = new MemoryFileSystemUrlHandlerProvider();
238+
239+
// When
240+
var handler = provider.createURLStreamHandler(url.getProtocol());
241+
242+
// Then
243+
assertThat(handler).isInstanceOf(Handler.class);
244+
}
245+
246+
@DisplayName("The provider returns a null handler for non 'memory' protocols")
247+
@ValueSource(strings = {
248+
"file://foo/bar/baz",
249+
"jar:file://foo/bar/baz.jar!/Bork.class",
250+
"jrt:",
251+
})
252+
@ParameterizedTest(name = "for URL {0}")
253+
void providerReturnsNullHandlerForOtherProtocol(String urlString) throws MalformedURLException {
254+
// Given
255+
var url = new URL(urlString);
256+
var provider = new MemoryFileSystemUrlHandlerProvider();
257+
258+
// When
259+
var handler = provider.createURLStreamHandler(url.getProtocol());
260+
261+
// Then
262+
assertThat(handler).isNull();
263+
}
264+
}
197265
}

java-compiler-testing/src/test/java/module-info.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,7 @@
3434
requires org.mockito;
3535
requires org.mockito.junit.jupiter;
3636
requires org.slf4j;
37+
38+
// Used for MemoryFileSystemProviderImplTest$MemoryFileSystemUrlHandlerProviderTest
39+
uses java.net.spi.URLStreamHandlerProvider;
3740
}

pom.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,6 @@
151151
-Dnet.bytebuddy.experimental=true
152152
-Dorg.slf4j.simpleLogger.log=INFO
153153
-Dorg.slf4j.simpleLogger.log.io.github.ascopes.jct=TRACE
154-
-Djava.protocol.handler.pkgs=com.github.marschall.memoryfilesystem
155154
-Xshare:off
156155
-XX:+TieredCompilation
157156
-XX:TieredStopAtLevel=1

0 commit comments

Comments
 (0)