Skip to content

Commit a5e8bb6

Browse files
Improve resilience of service loader
Avoid crashing if registered service cannot be loaded. This may lead to missing fields in the log output. But it will not keep an application from starting because of a wrongly registered class on the classpath. Signed-off-by: Karsten Schnitter <k.schnitter@sap.com>
1 parent 3fd90d7 commit a5e8bb6

File tree

3 files changed

+64
-1
lines changed

3 files changed

+64
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,40 @@
11
package com.sap.hcp.cf.logging.common.serialization;
22

3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
36
import java.util.ArrayList;
7+
import java.util.ServiceConfigurationError;
48
import java.util.ServiceLoader;
59
import java.util.stream.Collectors;
610
import java.util.stream.Stream;
711

812
public class ContextFieldSupplierServiceLoader<T> {
913

14+
private static Logger logger() {
15+
return LoggerFactory.getLogger(ContextFieldSupplierServiceLoader.class);
16+
}
17+
1018
private ContextFieldSupplierServiceLoader() {
1119
}
1220

1321
public static <T extends ContextFieldSupplier> ArrayList<T> addFieldSuppliers(Stream<T> original, Class<T> clazz) {
14-
Stream<T> spiSuppliers = ServiceLoader.load(clazz).stream().map(ServiceLoader.Provider::get).sorted();
22+
Stream<T> spiSuppliers = loadSafely(clazz);
1523
return Stream.concat(original, spiSuppliers).sorted().collect(Collectors.toCollection(ArrayList::new));
1624
}
1725

26+
private static <T extends ContextFieldSupplier> Stream<T> loadSafely(Class<T> clazz) {
27+
ArrayList<T> result = new ArrayList<>();
28+
var iterator = ServiceLoader.load(clazz).iterator();
29+
while (true) {
30+
try {
31+
if (!iterator.hasNext()) break;
32+
result.add(iterator.next());
33+
} catch (ServiceConfigurationError e) {
34+
logger().warn("Skipping invalid SPI provider for {}: {}", clazz.getName(), e.getMessage());
35+
}
36+
}
37+
return result.stream();
38+
}
39+
1840
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.sap.hcp.cf.logging.common.serialization;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import java.util.ArrayList;
6+
import java.util.Collections;
7+
import java.util.Map;
8+
import java.util.stream.Stream;
9+
10+
import static org.assertj.core.api.Assertions.assertThat;
11+
import static org.assertj.core.api.Assertions.assertThatCode;
12+
13+
class ContextFieldSupplierServiceLoaderTest {
14+
15+
public static class ValidSupplier implements ContextFieldSupplier {
16+
@Override
17+
public Map<String, Object> get() {
18+
return Collections.emptyMap();
19+
}
20+
}
21+
22+
@Test
23+
void doesNotThrowWhenSpiProviderIsInvalid() {
24+
assertThatCode(() -> ContextFieldSupplierServiceLoader.addFieldSuppliers(Stream.empty(),
25+
ContextFieldSupplier.class))
26+
.doesNotThrowAnyException();
27+
}
28+
29+
@Test
30+
void skipsInvalidSpiProviderAndKeepsValidOnes() {
31+
ValidSupplier valid = new ValidSupplier();
32+
33+
ArrayList<ContextFieldSupplier> result = ContextFieldSupplierServiceLoader.addFieldSuppliers(
34+
Stream.of(valid), ContextFieldSupplier.class);
35+
36+
assertThat(result).contains(valid);
37+
}
38+
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Intentionally invalid entry (does not implement ContextFieldSupplier) to trigger a ServiceConfigurationError for testing.
2+
com.sap.hcp.cf.logging.common.helper.StacktraceGenerator

0 commit comments

Comments
 (0)