Skip to content

Commit 2d1d7b5

Browse files
Throw a ServiceConfigurationError if the service is not JCA-compliant.
1 parent aa5db3f commit 2d1d7b5

File tree

2 files changed

+56
-56
lines changed

2 files changed

+56
-56
lines changed

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ServiceLoaderFeature.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
4242
import com.oracle.svm.core.feature.InternalFeature;
4343
import com.oracle.svm.core.jdk.ServiceCatalogSupport;
44-
import com.oracle.svm.core.option.HostedOptionKey;
4544
import com.oracle.svm.core.option.AccumulatingLocatableMultiOptionValue;
45+
import com.oracle.svm.core.option.HostedOptionKey;
4646
import com.oracle.svm.hosted.analysis.Inflation;
4747

4848
import jdk.graal.compiler.options.Option;
@@ -219,6 +219,8 @@ void handleServiceClassIsReachable(DuringAnalysisAccess access, Class<?> service
219219
RuntimeReflection.register(providerClass);
220220
if (nullaryConstructor != null) {
221221
RuntimeReflection.register(nullaryConstructor);
222+
} else {
223+
RuntimeReflection.registerConstructorLookup(providerClass);
222224
}
223225
if (nullaryProviderMethod != null) {
224226
RuntimeReflection.register(nullaryProviderMethod);
@@ -229,8 +231,14 @@ void handleServiceClassIsReachable(DuringAnalysisAccess access, Class<?> service
229231
*/
230232
RuntimeReflection.registerMethodLookup(providerClass, "provider");
231233
}
232-
registeredProviders.add(provider);
233234
}
235+
/*
236+
* Register the provider in both cases: when it is JCA-compliant (has a nullary
237+
* constructor or a provider method) or when it lacks both. If neither is present, a
238+
* ServiceConfigurationError will be thrown at runtime, consistent with HotSpot
239+
* behavior.
240+
*/
241+
registeredProviders.add(provider);
234242
}
235243
if (!registeredProviders.isEmpty()) {
236244
String serviceResourceLocation = "META-INF/services/" + serviceProvider.getName();
Lines changed: 46 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -40,12 +40,8 @@
4040
// Checkstyle: allow Class.getSimpleName
4141

4242
/**
43-
* Tests a workaround for {@linkplain ServiceLoader services} without a {@linkplain ServiceLoader
44-
* provider constructor} (nullary constructor) [GR-19958]. The workaround completely ignores
45-
* services without a provider constructor, instead of throwing an {@link ServiceConfigurationError}
46-
* when iterating the services. See the Github issue
47-
* <a href="https://github.com/oracle/graal/issues/2652">"Spring Service Registry native-image
48-
* failure due to service loader handling in jersey #2652"</a> for more details.
43+
* Test both JCA-compliant services and non-JCA-compliant services (without a nullary constructor),
44+
* and compare the behavior between Native Image and Hotspot.
4945
*/
5046
public class NoProviderConstructorServiceLoaderTest {
5147

@@ -73,81 +69,77 @@ public abstract static class NoProviderConstructorService implements ServiceInte
7369

7470
private static final Set<String> EXPECTED = Set.of(ProperService.class.getSimpleName());
7571

76-
/**
77-
* This should actually throw an {@link ServiceConfigurationError}.
78-
*
79-
* @see #testLazyStreamHotspot()
80-
*/
81-
@Test
72+
@Test(expected = ServiceConfigurationError.class)
8273
public void testLazyStreamNativeImage() {
83-
Assume.assumeTrue("native image specific behavior", ImageInfo.inImageRuntimeCode());
84-
Set<String> simpleNames = ServiceLoader.load(ServiceInterface.class).stream()
85-
.map(provider -> provider.type().getSimpleName())
86-
.collect(Collectors.toSet());
74+
assumeEnvironment(true);
75+
Set<String> simpleNames = loadLazyStreamNames();
8776
Assert.assertEquals(EXPECTED, simpleNames);
8877
}
8978

90-
/**
91-
* This should actually throw an {@link ServiceConfigurationError}.
92-
*
93-
* @see #testEagerStreamHotspot()
94-
*/
95-
@Test
79+
@Test(expected = ServiceConfigurationError.class)
9680
public void testEagerStreamNativeImage() {
97-
Assume.assumeTrue("native image specific behavior", ImageInfo.inImageRuntimeCode());
98-
Set<String> simpleNames = ServiceLoader.load(ServiceInterface.class).stream()
99-
.map(provider -> provider.get().getClass().getSimpleName())
100-
.collect(Collectors.toSet());
81+
assumeEnvironment(true);
82+
Set<String> simpleNames = loadEagerStreamNames();
10183
Assert.assertEquals(EXPECTED, simpleNames);
10284
}
10385

104-
/**
105-
* This should actually throw an {@link ServiceConfigurationError}.
106-
*
107-
* @see #testEagerIteratorHotspot()
108-
*/
109-
@Test
86+
@Test(expected = ServiceConfigurationError.class)
11087
public void testEagerIteratorNativeImage() {
111-
Assume.assumeTrue("native image specific behavior", ImageInfo.inImageRuntimeCode());
112-
Set<String> simpleNames = new HashSet<>();
113-
ServiceLoader.load(ServiceInterface.class).iterator()
114-
.forEachRemaining(s -> simpleNames.add(s.getClass().getSimpleName()));
88+
assumeEnvironment(true);
89+
Set<String> simpleNames = loadEagerIteratorNames();
11590
Assert.assertEquals(EXPECTED, simpleNames);
11691
}
11792

118-
/**
119-
* @see #testLazyStreamNativeImage()
120-
*/
12193
@Test(expected = ServiceConfigurationError.class)
12294
public void testLazyStreamHotspot() {
123-
Assume.assumeFalse("hotspot specific behavior", ImageInfo.inImageRuntimeCode());
124-
Set<String> simpleNames = ServiceLoader.load(ServiceInterface.class).stream()
95+
assumeEnvironment(false);
96+
Set<String> simpleNames = loadLazyStreamNames();
97+
Assert.assertNull("should not reach", simpleNames);
98+
}
99+
100+
@Test(expected = ServiceConfigurationError.class)
101+
public void testEagerIteratorHotspot() {
102+
assumeEnvironment(false);
103+
Set<String> simpleNames = loadEagerIteratorNames();
104+
Assert.assertNull("should not reach", simpleNames);
105+
}
106+
107+
/**
108+
* Helper method to assume the environment (hotspot/native image).
109+
*/
110+
private static void assumeEnvironment(boolean isNativeImage) {
111+
if (isNativeImage) {
112+
Assume.assumeTrue("native image specific behavior", ImageInfo.inImageRuntimeCode());
113+
} else {
114+
Assume.assumeFalse("hotspot specific behavior", ImageInfo.inImageRuntimeCode());
115+
}
116+
}
117+
118+
/**
119+
* Helper method for lazy stream tests.
120+
*/
121+
private static Set<String> loadLazyStreamNames() {
122+
return ServiceLoader.load(ServiceInterface.class).stream()
125123
.map(provider -> provider.type().getSimpleName())
126124
.collect(Collectors.toSet());
127-
Assert.assertNull("should not reach", simpleNames);
128125
}
129126

130127
/**
131-
* @see #testEagerStreamNativeImage()
128+
* Helper method for eager stream tests.
132129
*/
133-
@Test(expected = ServiceConfigurationError.class)
134-
public void testEagerStreamHotspot() {
135-
Assume.assumeFalse("hotspot specific behavior", ImageInfo.inImageRuntimeCode());
136-
Set<String> simpleNames = ServiceLoader.load(ServiceInterface.class).stream()
130+
private static Set<String> loadEagerStreamNames() {
131+
return ServiceLoader.load(ServiceInterface.class).stream()
137132
.map(provider -> provider.get().getClass().getSimpleName())
138133
.collect(Collectors.toSet());
139-
Assert.assertNull("should not reach", simpleNames);
140134
}
141135

142136
/**
143-
* @see #testEagerIteratorNativeImage()
137+
* Helper method for eager iterator tests.
144138
*/
145-
@Test(expected = ServiceConfigurationError.class)
146-
public void testEagerIteratorHotspot() {
147-
Assume.assumeFalse("hotspot specific behavior", ImageInfo.inImageRuntimeCode());
139+
private static Set<String> loadEagerIteratorNames() {
148140
Set<String> simpleNames = new HashSet<>();
149141
ServiceLoader.load(ServiceInterface.class).iterator()
150142
.forEachRemaining(s -> simpleNames.add(s.getClass().getSimpleName()));
151-
Assert.assertNull("should not reach", simpleNames);
143+
return simpleNames;
152144
}
153145
}

0 commit comments

Comments
 (0)