ExtensionAdderProviders.java
/**
* Copyright (c) 2020, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* SPDX-License-Identifier: MPL-2.0
*/
package com.powsybl.commons.extensions;
import java.util.Collections;
import java.util.List;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Suppliers;
import com.powsybl.commons.PowsyblException;
/**
* A utility class to help finding providers using ServiceLoader.
*
* @author Jon Harper {@literal <jon.harper at rte-france.com>}
*
*/
//Don't bother with generics because serviceloader doesn't return them
//and we put them in a cache where we can't propagate the generic types.
@SuppressWarnings("rawtypes")
public final class ExtensionAdderProviders {
private static final Logger LOGGER = LoggerFactory.getLogger(ExtensionAdderProviders.class);
private static final Supplier<ConcurrentMap<String, List<ExtensionAdderProvider>>> ADDER_PROVIDERS = Suppliers
.memoize(() -> groupProvidersByName(ServiceLoader.load(ExtensionAdderProvider.class, ExtensionAdderProviders.class.getClassLoader())));
private static final ConcurrentMap<Pair<String, Class>, ExtensionAdderProvider> BY_CLASS_CACHE = new ConcurrentHashMap<>();
private static final ConcurrentMap<Pair<String, String>, ExtensionAdderProvider> BY_NAME_CACHE = new ConcurrentHashMap<>();
//package private for tests
static ConcurrentMap<String, List<ExtensionAdderProvider>> groupProvidersByName(Iterable<ExtensionAdderProvider> i) {
return StreamSupport.stream(i.spliterator(), false).collect(Collectors
.groupingByConcurrent(ExtensionAdderProvider::getImplementationName));
}
private ExtensionAdderProviders() {
}
private static <O, E extends Extension<O>, B extends ExtensionAdder<O, E>> ExtensionAdderProvider findProvider(
String implName, Class<B> extensionAdderType,
ConcurrentMap<String, List<ExtensionAdderProvider>> providersMap) {
return findProvider(implName, s -> extensionAdderType.isAssignableFrom(s.getAdderClass()), extensionAdderType.getSimpleName(), providersMap);
}
private static ExtensionAdderProvider findProvider(
String implName, String extensionName,
ConcurrentMap<String, List<ExtensionAdderProvider>> providersMap) {
return findProvider(implName, s -> s.getExtensionName() != null && extensionName.equals(s.getExtensionName()), extensionName, providersMap);
}
private static ExtensionAdderProvider findProvider(
String implName, Predicate<ExtensionAdderProvider> typeFilter, String typeName,
ConcurrentMap<String, List<ExtensionAdderProvider>> providersMap) {
List<ExtensionAdderProvider> providersForName = providersMap.get(implName);
if (providersForName == null) {
providersForName = Collections.emptyList();
}
List<ExtensionAdderProvider> providers = providersForName.stream()
.filter(typeFilter)
.toList();
if (providers.isEmpty()) {
LOGGER.error(
"ExtensionAdderProvider not found for ExtensionAdder {} for implementation {}",
typeName, implName);
throw new PowsyblException("ExtensionAdderProvider not found");
}
if (providers.size() > 1) {
LOGGER.error(
"Multiple ExtensionAdderProviders found for ExtensionAdder {} for implementation {} : {}",
typeName, implName, providers);
throw new PowsyblException(
"Multiple ExtensionAdderProviders configuration providers found");
}
return providers.get(0);
}
// TODO use O extends Extendable<O> here ? For now we can't because Extendable
// doesn't declare Extendable<O extends Extendable>
// package private for tests
static <O, E extends Extension<O>, B extends ExtensionAdder<O, E>> ExtensionAdderProvider findCachedProvider(
String implName, Class<B> type,
ConcurrentMap<String, List<ExtensionAdderProvider>> providersMap,
ConcurrentMap<Pair<String, Class>, ExtensionAdderProvider> cache) {
return cache.computeIfAbsent(Pair.of(implName, type), k -> findProvider(implName, type, providersMap));
}
static ExtensionAdderProvider findCachedProvider(
String implName, String typeName,
ConcurrentMap<String, List<ExtensionAdderProvider>> providersMap,
ConcurrentMap<Pair<String, String>, ExtensionAdderProvider> cache) {
return cache.computeIfAbsent(Pair.of(implName, typeName), k -> findProvider(implName, typeName, providersMap));
}
public static <O, E extends Extension<O>, B extends ExtensionAdder<O, E>> ExtensionAdderProvider findCachedProvider(
String implName, Class<B> type) {
return findCachedProvider(implName, type, ADDER_PROVIDERS.get(), BY_CLASS_CACHE);
}
public static ExtensionAdderProvider findCachedProvider(String implName, String typeName) {
return findCachedProvider(implName, typeName, ADDER_PROVIDERS.get(), BY_NAME_CACHE);
}
}