-
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
6 changed files
with
605 additions
and
0 deletions.
There are no files selected for viewing
39 changes: 39 additions & 0 deletions
39
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,39 @@ | ||
/** | ||
* 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 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 Extendable#createExtension} that must be overriden by | ||
* subclasses to create the extension. | ||
* | ||
* @author Jon Harper <jon.harper at rte-france.com> | ||
*/ | ||
//TODO can we use T extends Extendable<T> here ? Then we can remove the cast in add() | ||
public abstract class AbstractExtensionAdder<T, E extends Extension<T>> implements ExtensionAdder<T, E> { | ||
|
||
protected T extendable; | ||
|
||
protected AbstractExtensionAdder(T extendable) { | ||
this.extendable = extendable; | ||
} | ||
|
||
/** | ||
* Creates the extension. | ||
* | ||
* @return the extension | ||
*/ | ||
protected abstract E createExtension(); | ||
|
||
@Override | ||
public T add() { | ||
((Extendable<T>) extendable).addExtension(getExtensionInterface(), createExtension()); | ||
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
41 changes: 41 additions & 0 deletions
41
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,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; | ||
|
||
/** | ||
* 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. | ||
* | ||
* @return the interface of the extension | ||
*/ | ||
// Class<? super E> to allow builders 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> getExtensionInterface(); | ||
|
||
/** | ||
* 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(); | ||
} |
49 changes: 49 additions & 0 deletions
49
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,49 @@ | ||
/** | ||
* 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 | ||
*/ | ||
//TODO can we use T extends Extendable<T> here ? It would make calling newAdder safer. | ||
public interface ExtensionAdderProvider<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); | ||
} |
94 changes: 94 additions & 0 deletions
94
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,94 @@ | ||
/** | ||
* 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") | ||
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; | ||
|
||
//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 static final ConcurrentMap<Pair<String, Class>, ExtensionAdderProvider> CACHE = new ConcurrentHashMap<>(); | ||
|
||
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> | ||
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)); | ||
} | ||
|
||
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.