Skip to content

Commit ba38525

Browse files
authored
refactor(core): improve safety of service loader usage (#369)
1 parent 17a3039 commit ba38525

File tree

1 file changed

+49
-5
lines changed

1 file changed

+49
-5
lines changed

core/src/main/java/io/github/xanthic/cache/core/CacheApiSettings.java

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,19 @@
1111
import org.jetbrains.annotations.NotNull;
1212
import org.jetbrains.annotations.Nullable;
1313

14+
import java.security.AccessControlException;
15+
import java.security.AccessController;
16+
import java.security.PrivilegedAction;
1417
import java.util.Collections;
1518
import java.util.Comparator;
1619
import java.util.IdentityHashMap;
20+
import java.util.Iterator;
1721
import java.util.Map;
22+
import java.util.ServiceConfigurationError;
1823
import java.util.ServiceLoader;
24+
import java.util.SortedSet;
25+
import java.util.TreeSet;
1926
import java.util.concurrent.atomic.AtomicReference;
20-
import java.util.stream.StreamSupport;
2127

2228
/**
2329
* Holds a registry of default settings and cache providers.
@@ -104,10 +110,36 @@ public void registerCacheProvider(@NonNull Class<? extends CacheProvider> cacheP
104110
private void populateProviders() {
105111
log.debug("Xanthic: Registering canonical cache providers from the classpath...");
106112

107-
ServiceLoader<AbstractCacheProvider> load = ServiceLoader.load(AbstractCacheProvider.class);
108-
StreamSupport.stream(load.spliterator(), false)
109-
.sorted(Comparator.comparingInt(AbstractCacheProvider::getDiscoveryOrder))
110-
.forEach(provider -> registerCacheProvider(provider.getClass(), provider));
113+
// prepare service loader
114+
ServiceLoader<AbstractCacheProvider> loader;
115+
try {
116+
loader = getServiceLoader();
117+
} catch (ServiceConfigurationError e) {
118+
log.error("Failed to create CacheProvider service loader!", e);
119+
return;
120+
}
121+
122+
SortedSet<AbstractCacheProvider> loaded = new TreeSet<>(
123+
Comparator.comparingInt(AbstractCacheProvider::getDiscoveryOrder)
124+
.thenComparing(provider -> provider.getClass().getName())
125+
.thenComparingInt(Object::hashCode)
126+
);
127+
128+
// instantiate providers
129+
Iterator<AbstractCacheProvider> it = loader.iterator();
130+
while (it.hasNext()) {
131+
AbstractCacheProvider provider;
132+
try {
133+
provider = it.next();
134+
} catch (ServiceConfigurationError | AccessControlException e) {
135+
log.error("Failed to instantiate cache provider via service loader!", e);
136+
continue;
137+
}
138+
loaded.add(provider);
139+
}
140+
141+
// register providers
142+
loaded.forEach(provider -> registerCacheProvider(provider.getClass(), provider));
111143

112144
log.debug("Xanthic: Loaded {} canonical cache provider(s) on settings construction!", providers.size());
113145
}
@@ -120,6 +152,18 @@ public static CacheApiSettings getInstance() {
120152
return SingletonHolder.INSTANCE;
121153
}
122154

155+
private static ServiceLoader<AbstractCacheProvider> getServiceLoader() {
156+
ClassLoader classLoader = CacheApiSettings.class.getClassLoader();
157+
158+
SecurityManager securityManager = System.getSecurityManager();
159+
if (securityManager != null) {
160+
PrivilegedAction<ServiceLoader<AbstractCacheProvider>> action = () -> ServiceLoader.load(AbstractCacheProvider.class, classLoader);
161+
return AccessController.doPrivileged(action);
162+
}
163+
164+
return ServiceLoader.load(AbstractCacheProvider.class, classLoader);
165+
}
166+
123167
private static class SingletonHolder {
124168
private static final CacheApiSettings INSTANCE = new CacheApiSettings();
125169
}

0 commit comments

Comments
 (0)