-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow to have different implementations of extensions
Signed-off-by: Jon Harper <jon.harper87@gmail.com>
- Loading branch information
Showing
10 changed files
with
634 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
commons/src/main/java/com/powsybl/commons/extensions/AbstractExtensionAdder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
/** | ||
* 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/. | ||
*/ | ||
package com.powsybl.commons.extensions; | ||
|
||
import java.util.Objects; | ||
|
||
/** | ||
* A base class for implementations of {@link ExtensionAdder} that holds the | ||
* extendable to be able build and then add the extension to the extendable. | ||
* This class calls {@link #createExtension} that must be overriden by | ||
* subclasses to create the extension. | ||
* | ||
* @author Jon Harper <jon.harper at rte-france.com> | ||
*/ | ||
public abstract class AbstractExtensionAdder<T extends Extendable<T>, E extends Extension<T>> | ||
implements ExtensionAdder<T, E> { | ||
|
||
private final T extendable; | ||
|
||
protected AbstractExtensionAdder(T extendable) { | ||
this.extendable = Objects.requireNonNull(extendable); | ||
} | ||
|
||
/** | ||
* Creates the extension. | ||
* | ||
* @return the extension | ||
*/ | ||
protected abstract E createExtension(T extendable); | ||
|
||
@Override | ||
public T add() { | ||
extendable.addExtension(getExtensionClass(), createExtension(extendable)); | ||
return extendable; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 42 additions & 0 deletions
42
commons/src/main/java/com/powsybl/commons/extensions/ExtensionAdder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/** | ||
* 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/. | ||
*/ | ||
package com.powsybl.commons.extensions; | ||
|
||
/** | ||
* An ExtensionAdder is a builder for an extension that is built and then added | ||
* to an extendable. | ||
* | ||
* @author Jon Harper <jon.harper at rte-france.com> | ||
*/ | ||
// Can't use "T extends Extendable<T>" here because T is used in Extendable::newExtensionAdder | ||
// to ensure that ExtensionAdder::T is the same as Extendable::O and Extendable doesn't declare | ||
// Extendable<O extends Extendable>. | ||
// TODO: If we can, we should change Extendable to declare Extendable<O extends Extendable> ? | ||
public interface ExtensionAdder<T, E extends Extension<T>> { | ||
|
||
/** | ||
* Returns the class of the extension. This is expected to be an interface so | ||
* that multiple implementors can implement the same extensions. This will be | ||
* the key at which the extension is added on the extendable. This is meant to | ||
* be implemented by adder interfaces but not by adder implementations. | ||
* | ||
* @return the interface of the extension | ||
*/ | ||
// Class<? super E> to allow adders of generic extensions to return the class | ||
// of the raw type. This has the bad effect of allowing the super classes but we | ||
// consider the trade-off acceptable. | ||
Class<? super E> getExtensionClass(); | ||
|
||
/** | ||
* Builds and adds the extension to the extendable which was used to get this | ||
* extensionAdder. The extendable is returned to allow a fluent style adding of | ||
* multiple extensions. | ||
* | ||
* @return the extendable | ||
*/ | ||
T add(); | ||
} |
48 changes: 48 additions & 0 deletions
48
commons/src/main/java/com/powsybl/commons/extensions/ExtensionAdderProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
/** | ||
* 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/. | ||
*/ | ||
package com.powsybl.commons.extensions; | ||
|
||
/** | ||
* A provider used through java's {@link java.util.ServiceLoader}. It will | ||
* provide an {@link ExtensionAdder} to add an extension to an extendable. | ||
* {@link #getImplementationName} is used to find providers corresponding to the | ||
* implementation of the {@link Extendable}. {@link #getAdderClass} is used to | ||
* specify the adder class. | ||
* | ||
* @author Jon Harper <jon.harper at rte-france.com> | ||
* | ||
* @param <T> The extendable | ||
* @param <E> The extension | ||
* @param <B> The extensionBuilder | ||
*/ | ||
public interface ExtensionAdderProvider<T extends Extendable<T>, E extends Extension<T>, B extends ExtensionAdder<T, E>> { | ||
|
||
/** | ||
* Returns a name that is used to select this provider when searching for | ||
* implementations of extension builders in {@link Extendable#newExtension}. | ||
* | ||
* @return the name | ||
*/ | ||
String getImplementationName(); | ||
|
||
/** | ||
* Returns the builder class provided by this Provider. | ||
* | ||
* @return the class | ||
*/ | ||
// Class<? super B> to allow providers of generic builders to return the class | ||
// of the raw type. This has the bad effect of allowing the super classes but we | ||
// consider the trade-off acceptable. | ||
Class<? super B> getAdderClass(); | ||
|
||
/** | ||
* returns an new empty ExtensionAdder for this extendable. | ||
* | ||
* @param extendable the extendable on which the adder will add the extension | ||
*/ | ||
B newAdder(T extendable); | ||
} |
95 changes: 95 additions & 0 deletions
95
commons/src/main/java/com/powsybl/commons/extensions/ExtensionAdderProviders.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
/** | ||
* 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/. | ||
*/ | ||
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.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 <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)))::get; | ||
|
||
private static final ConcurrentMap<Pair<String, Class>, ExtensionAdderProvider> 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 name, Class<B> type, | ||
ConcurrentMap<String, List<ExtensionAdderProvider>> providersMap) { | ||
|
||
List<ExtensionAdderProvider> providersForName = providersMap.get(name); | ||
if (providersForName == null) { | ||
providersForName = Collections.emptyList(); | ||
} | ||
List<ExtensionAdderProvider> providers = providersForName.stream() | ||
.filter(s -> type.isAssignableFrom(s.getAdderClass())) | ||
.collect(Collectors.toList()); | ||
|
||
if (providers.isEmpty()) { | ||
LOGGER.error( | ||
"ExtensionAdderProvider not found for ExtensionAdder {} for implementation {}", | ||
type.getSimpleName(), name); | ||
throw new PowsyblException("ExtensionAdderProvider not found"); | ||
} | ||
|
||
if (providers.size() > 1) { | ||
LOGGER.error( | ||
"Multiple ExtensionAdderProviders found for ExtensionAdder {} for implementation {} : {}", | ||
type.getSimpleName(), name, providers); | ||
throw new PowsyblException( | ||
"Multiple platform 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 name, Class<B> type, | ||
ConcurrentMap<String, List<ExtensionAdderProvider>> providersMap, | ||
ConcurrentMap<Pair<String, Class>, ExtensionAdderProvider> cache) { | ||
return cache.computeIfAbsent(Pair.of(name, type), k -> findProvider(name, type, providersMap)); | ||
} | ||
|
||
public static <O, E extends Extension<O>, B extends ExtensionAdder<O, E>> ExtensionAdderProvider findCachedProvider( | ||
String name, Class<B> type) { | ||
return findCachedProvider(name, type, ADDER_PROVIDERS.get(), CACHE); | ||
} | ||
} |
Oops, something went wrong.